Detection rules › Kusto
Kusto rules: 1n
[Entra ID] Privileged Role Assigned to User
#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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Privilege Escalation | |
| Stealth |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | any: Entra ID audit event (any operation) |
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions (Kusto)
- [Entra ID] Mass Privileged Role Change Activity Detected (Kusto)
- [Entra ID] Privilege Elevation Request Denied (Kusto)
- [Entra ID] Privileged Role Assigned to a New User (Kusto)
- Account Elevated to New Role (Kusto)
- Admin promotion after Role Management Application Permission Grant (Kusto)
- App Assigned To Azure RBAC/Microsoft Entra Role (Sigma)
- Azure AD Application Administrator Role Assigned (Splunk)
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.
| Field | Kind | Values | Search |
|---|---|---|---|
AADOperationType | in |
| field:"AADOperationType" kind:in |
ActivityDisplayName | match |
| field:"azure_ad::activity_display_name" kind:match |
Category | eq |
| field:"Category" kind:eq value:"RoleManagement" |
Initiator | ne |
| field:"Initiator" kind:ne value:"MS-PIM" |
Property.displayName | eq |
| field:"Property.displayName" kind:eq value:"Role.DisplayName" |
RoleName | contains |
| field:"RoleName" kind:contains value:"Admin" |
TargetResourceRole.type | in |
| field:"TargetResourceRole.type" kind:in value:"Role" |
TargetResourceSubject.type | in |
| field:"TargetResourceSubject.type" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Alert_Category_en | project |
Alert_Containment_en | project |
Alert_Description_en | project |
Alert_Name_en | project |
Alert_Remediation_en | project |
Alert_SubCategory_en | project |
Alert_Time_TW | project |
Alert_Time_UTC0 | project |
Alert_TriageStep_en | project |
Event_Action | project |
Event_Status | project |
Source_Identity_FullName | project |
Source_Identity_Type | project |
Source_Network_IPAddress | project |
Source_Network_IPLocation | project |
Target_Identity_DomainType | project |
Target_Identity_FullName | project |
Target_Identity_Type | project |
Target_Resource_ID | project |
Target_Resource_Type | project |
ADFS DKM Master Key Export
#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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"LdapSearch" |
AdditionalFields.AttributeList | contains |
| field:"AdditionalFields.AttributeList" kind:contains value:"thumbnailPhoto" |
AdditionalFields.DistinguishedName | contains |
| field:"AdditionalFields.DistinguishedName" kind:contains value:"CN=ADFS,CN=Microsoft,CN=Program Data" |
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4662" |
ObjectServer | eq |
| field:"ObjectServer" kind:eq value:"DS" |
ObjectType | contains |
| field:"ObjectType" kind:contains value:"5cb41ed0-0e4c-11d0-a286-00aa003049e2" |
OperationType | eq |
| field:"OperationType" kind:eq value:"Object Access" |
Properties | contains |
| field:"Properties" kind:contains value:"8d3bca50-1d7e-11d0-a081-00aa006c33ed" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountDomain | extend |
AccountName | extend |
DeviceName | extend |
timestamp | extend |
ObjectServer | extend |
OperationType | extend |
ObjectType | extend |
Properties | extend |
Account | extend |
ApexOne - Spyware with failed response
#'Detects spyware with failed response.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionResult | match |
| field:"ActionResult" kind:match |
EventMessage | match |
| field:"EventMessage" kind:match value:"Spyware Detected" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IPCustomEntity | extend |
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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| 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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| 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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"AzureT0VMContributor" |
Claroty - Asset Down
#'Triggers asset is down.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
EventOriginalType | match |
| field:"EventOriginalType" kind:match value:"Asset Down" |
EventType | match |
| field:"EventType" kind:match value:"Asset Down" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
TimeGenerated | project |
IPCustomEntity | extend |
Contrast Exploits
#'Creates Incidents for Exploit events sourced from the Contrast Protect agent.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
AdditionalExtensions | contains |
| field:"AdditionalExtensions" kind:contains value:"EXPLOITED" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Contrast Security" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceProduct | extend |
SourceIP | extend |
DeviceVersion | extend |
Activity | extend |
ApplicationProtocol | extend |
RequestURL | extend |
RequestMethod | extend |
Rule | extend |
Linked Malicious Storage Artifacts
#'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
| Tactic | Techniques |
|---|---|
| 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
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 =DisplayName has "file""File""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.
| Field | Kind | Values | Search |
|---|---|---|---|
DisplayName | match |
| field:"DisplayName" kind:match value:"Potential malware uploaded to" |
Entities["Type"] | eq |
| |
OperationName | eq |
| field:"OperationName" kind:eq |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertTimeGenerated | project |
AlertType | project |
AttackerCountry | project |
AttackerIP | project |
FilesUploaded | project |
HashAlgorithm | project |
LinkedMaliciousFileHash | project |
LinkedMaliciousFileName | project |
MaliciousFileDirectory | project |
MaliciousFileName | project |
UploadedFileInfo | project |
Lookout - Critical Audit and Policy Changes (v2)
#'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
| Tactic | Techniques |
|---|---|
| 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 =AuditType == "POLICY_CHANGE""High"AuditType == "SECURITY_SETTING_CHANGE""High"AuditType == "USER_MANAGEMENT""Medium"AuditType == "CONFIGURATION_CHANGE""Medium""Low"RiskLevel =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"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.
| Field | Kind | Values | Search |
|---|---|---|---|
AuditType | in |
| field:"AuditType" kind:in |
EventType | eq |
| field:"EventType" kind:eq value:"AUDIT" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActorGuid | project |
ActorType | project |
AuditAttributeChanges | project |
AuditType | project |
ChangeDetails | project |
ChangeImpact | project |
ChangeType | project |
ComplianceRisk | project |
EnterpriseGuid | project |
EventId | project |
RiskLevel | project |
SecurityImplications | project |
TargetEmailAddress | project |
TargetGuid | project |
TargetType | project |
TimeGenerated | project |
Pathlock TDnR - Credit Card Data Changes
#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
| Tactic | Techniques |
|---|---|
| Collection | |
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
DataSource | eq |
| field:"DataSource" kind:eq value:"CHANGEDOC_CCARD" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AffectedUser | project |
Area | project |
Bname | project |
CentralTs | project |
DataSource | project |
DestIp | project |
Eventid | project |
Hostname | project |
Instance | project |
LogLine | project |
MessageV1 | project |
MessageV2 | project |
MessageV3 | project |
MessageV4 | project |
MsgId | project |
MsgNo | project |
MsgType | project |
Report | project |
SrcIp | project |
Subid | project |
Sysid | project |
Tcode | project |
TimeGenerated | project |
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.
When a Knox device boot binary is at risk of compromise.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
MitreTtp | match |
| field:"MitreTtp" kind:match value:"T1645" |
Name | eq |
| field:"Name" kind:eq value:"BOOT_COMPROMISED_SOFTWARE_BINARY" |
Sign-ins from IPs that attempt sign-ins to disabled accounts
#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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence |
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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
ActivityType | in |
| field:"ActivityType" kind:in |
EventSource | eq |
| field:"EventSource" kind:eq value:"Azure AD" |
SFRatio | ge |
| field:"SFRatio" kind:ge value:"0.5" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPAddress | summarize |
Suspicious MSC File Launched
#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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Stealth |
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
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
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
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
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
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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq |
FileName | ends_with |
| field:"FileName" kind:ends_with value:".msc" |
FileName | eq |
| field:"FileName" kind:eq value:"mmc.exe" |
FileOriginUrl | is_not_null | field:"FileOriginUrl" kind:is_not_null | |
FolderPath | contains |
| field:"FolderPath" kind:contains |
FolderPath | regex_match |
| field:"FolderPath" kind:regex_match value:"((?i)[^=|\/]*?AppData\Local\Temp\(7z.........\|wz....\|Temp\d{1,3}_\w+)\b[^( ;)|]*)" |
InitiatingProcessFileName | in |
| field:"InitiatingProcessFileName" kind:in |
ParsedCommandLine | contains |
| field:"ParsedCommandLine" kind:contains value:".msc" |
PreviousFileName | ends_with |
| field:"PreviousFileName" kind:ends_with value:".crdownload" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ParsedCommandLine | extend |
MscFile | extend |
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.
'Detects high severity alerts from ZeroFox'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | in |
| field:"Severity" kind:in value:"5" |
Zscaler - ZPA connections by new user
#'Detects ZPA connections by new user.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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
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.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstUserName | eq | listUsers | excludes:DstUserName field:"DstUserName" value:"listUsers" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | summarize |
AccountCustomEntity | extend |