Detection rules › Kusto
Kusto rules: 2v
1Password - Non-privileged vault user permission change
#This will alert when user permissions have changed within a non-privileged vault which have been implemented by an actor that was not the target user account. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same. Ref: https://1password.com/ Ref: https://github.com/securehats/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| 1Password | any: User vault access (catch-all) |
Rule Body
id: 327e0579-7c03-4ec7-acf5-a29dcc4a12b6
name: 1Password - Non-privileged vault user permission change
version: 1.0.0
kind: Scheduled
description: |-
This will alert when user permissions have changed within a non-privileged vault which have been implemented by an actor that was not the target user account. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same.
Ref: https://1password.com/
Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
- connectorId: 1Password
dataTypes:
- OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
query: |-
let watchlist =
_GetWatchlist("PV1PW")
| project SearchKey
;
// Insert the vault UUIDs below when using the dynamic vaults list within the analytics rule itself
let vaults = dynamic([""]);
OnePasswordEventLogs_CL
| where log_source == "auditevents"
| where action has_any("grant", "revoke", "update")
| where object_type == "uva"
| where tostring(actor_details.email) != tostring(aux_details.email)
// Enable the line below when using the "Privileged Vaults - 1PW" watchlist
| where object_uuid !in (watchlist)
// Enable the line below when using the dynamic vaults list within the analytics rule itself
// | where object_uuid !in (vaults)
| extend
TargetUsername = aux_details.email
, ActorUsername = actor_details.email
, SrcIpAddr = session.ip
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorUsername
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUsername
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
Stages and Predicates
Parameters
let vaults = dynamic([""]);
Let Binding: watchlist
let watchlist = _GetWatchlist("PV1PW")
| project SearchKey;
Stage 1: source
OnePasswordEventLogs_CL
Stage 2: where
| where log_source == "auditevents"
Stage 3: where
| where action has_any("grant", "revoke", "update")
Stage 4: where
| where object_type == "uva"
Stage 5: where
| where tostring(actor_details.email) != tostring(aux_details.email)
Stage 6: where
| where object_uuid !in (watchlist)
Stage 7: extend
| extend
TargetUsername = aux_details.email
, ActorUsername = actor_details.email
, SrcIpAddr = session.ip
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | match |
| field:"action" kind:match |
log_source | eq |
| field:"log_source" kind:eq value:"auditevents" |
object_type | eq |
| field:"object_type" kind:eq value:"uva" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
object_uuid | eq | watchlist | excludes:object_uuid field:"object_uuid" value:"watchlist" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActorUsername | extend |
SrcIpAddr | extend |
TargetUsername | extend |
A host is potentially running PowerShell to send HTTP(S) requests (ASIM Web Session schema)
#'This rule identifies a web request with a user agent header known to belong PowerShell. <br>You can add custom Powershell indicating User-Agent headers using a watchlist, for more information refer to the UnusualUserAgents Watchlist.<br><br> This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM WebSession schema (ASIM WebSession Schema)'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth | |
| Command & Control |
Rule Body
id: 42436753-9944-4d70-801c-daaa4d19ddd2
name: A host is potentially running PowerShell to send HTTP(S) requests (ASIM Web Session schema)
description: |
'This rule identifies a web request with a user agent header known to belong PowerShell. <br>You can add custom Powershell indicating User-Agent headers using a watchlist, for more information refer to the [UnusualUserAgents Watchlist](https://aka.ms/ASimUnusualUserAgentsWatchlist).<br><br>
This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM WebSession schema (ASIM WebSession Schema)'
severity: Medium
tags:
- ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/CiscoUmbrella/CiscoUmbrellaPowershellUserAgentDetected.yaml
version: 1.0.0
- Schema: ASimWebSession
SchemaVersion: 0.2.1
requiredDataConnectors:
- connectorId: SquidProxy
dataTypes:
- SquidProxy_CL
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- DefenseEvasion
- Execution
relevantTechniques:
- T1132
- T1140
- T1059.001
query: |
let threatCategory="Powershell";
let knownUserAgentsIndicators = materialize(externaldata(UserAgent:string, Category:string)
[ @"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"]
with(format="csv", ignoreFirstRecord=True));
let knownUserAgents=toscalar(knownUserAgentsIndicators | where Category==threatCategory | where isnotempty(UserAgent) | summarize make_list(UserAgent));
let customUserAgents=toscalar(_GetWatchlist("UnusualUserAgents") | where SearchKey==threatCategory | extend UserAgent=column_ifexists("UserAgent","") | where isnotempty(UserAgent) | summarize make_list(UserAgent));
let fullUAList = array_concat(knownUserAgents,customUserAgents);
_Im_WebSession(httpuseragent_has_any=fullUAList)
| project SrcIpAddr, Url, TimeGenerated, HttpUserAgent, SrcUsername
| extend AccountName = tostring(split(SrcUsername, "@")[0]), AccountUPNSuffix = tostring(split(SrcUsername, "@")[1])
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SrcUsername
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
alertDetailsOverride:
alertDisplayNameFormat: 'Host {{SrcIpAddr}} is potentially running PowerShell'
alertDescriptionFormat: 'The host at address {{SrcIpAddr}} sent an HTTP request to the URL {{Url}} with the HTTP user agent header {{HttpUserAgent}}. This user agent is known to be used by PowerShell and indicates suspicious activity on the host.'
customDetails:
UserAgent: HttpUserAgent
eventGroupingSettings:
aggregationKind: AlertPerResult
version: 1.1.5
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Yaron
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Parameters
let threatCategory = "Powershell";
let fullUAList = array_concat(knownUserAgents,customUserAgents);
Let Binding: knownUserAgentsIndicators
let knownUserAgentsIndicators = materialize(externaldata(UserAgent:string, Category:string)
[ @"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"]
with(format="csv", ignoreFirstRecord=True));
Let Binding: knownUserAgents
let knownUserAgents = toscalar(knownUserAgentsIndicators | where Category==threatCategory | where isnotempty(UserAgent) | summarize make_list(UserAgent));
Let Binding: customUserAgents
let customUserAgents = toscalar(_GetWatchlist("UnusualUserAgents") | where SearchKey==threatCategory | extend UserAgent=column_ifexists("UserAgent","") | where isnotempty(UserAgent) | summarize make_list(UserAgent));
Stage 1: source
_Im_WebSession(httpuseragent_has_any=fullUAList)
Stage 2: project
| project SrcIpAddr, Url, TimeGenerated, HttpUserAgent, SrcUsername
Stage 3: extend
| extend AccountName = tostring(split(SrcUsername, "@")[0]), AccountUPNSuffix = tostring(split(SrcUsername, "@")[1])
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HttpUserAgent | project |
SrcIpAddr | project |
SrcUsername | project |
TimeGenerated | project |
Url | project |
AccountName | extend |
AccountUPNSuffix | extend |
BloodHound Attack Path Finding - Large Default Groups With WriteDacl Privilege
#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 WriteDacl Privilege
Rule Body
id: cfa3b93b-0203-4dd1-b784-c148af74e389
name: BloodHound Attack Path Finding - Large Default Groups With WriteDacl Privilege
description: BloodHound Attack Path Finding - Large Default Groups With WriteDacl
Privilege
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 == "LargeDefaultGroupsWriteDacl"
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 == "LargeDefaultGroupsWriteDacl"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsWriteDacl" |
Cross-Cloud Suspicious user activity observed in GCP Envourment
#'This detection query aims to correlate potentially suspicious user activities logged in Google Cloud Platform (GCP) Audit Logs with security alerts originating from Microsoft Security products. This correlation facilitates the identification of potential cross-cloud security incidents. By summarizing these findings, the query provides valuable insights into cross-cloud identity threats and their associated details, enabling organizations to respond promptly and mitigate potential risks effectively.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Credential Access | |
| Discovery |
Rule Body
id: 58e306fe-1c49-4b8f-9b0e-15f25e8f0cd7
name: Cross-Cloud Suspicious user activity observed in GCP Envourment
description: |
'This detection query aims to correlate potentially suspicious user activities logged in Google Cloud Platform (GCP) Audit Logs with security alerts originating from Microsoft Security products. This correlation facilitates the identification of potential cross-cloud security incidents. By summarizing these findings, the query provides valuable insights into cross-cloud identity threats and their associated details, enabling organizations to respond promptly and mitigate potential risks effectively.'
severity: Medium
requiredDataConnectors:
- connectorId: GCPAuditLogsDefinition
dataTypes:
- GCPAuditLogs
- connectorId: AzureActiveDirectoryIdentityProtection
dataTypes:
- SecurityAlert (IPC)
- connectorId: MicrosoftThreatProtection
dataTypes:
- SecurityAlert
- connectorId: MicrosoftDefenderAdvancedThreatProtection
dataTypes:
- SecurityAlert (MDATP)
- connectorId: MicrosoftCloudAppSecurity
dataTypes:
- SecurityAlert
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
- Persistence
- PrivilegeEscalation
- CredentialAccess
- Discovery
relevantTechniques:
- T1566
- T1059
- T1078
- T1046
- T1547
- T1548
- T1069
- T1552
query: |
// Filter GCP Audit Logs to exclude service accounts
GCPAuditLogs
| where PrincipalEmail !endswith "gserviceaccount.com"
// Exclude system-related authentication information
| where AuthenticationInfo !has ("system:")
// Extract GCP request name and relevant attributes
| extend GCPRequestName= parse_json(Request).name
| extend
GCPAccoutType= tostring(split(GCPRequestName, "/")[2]),
GCPUserIdentity = iff(isempty(tostring(split(GCPRequestName, "/")[3])), tostring(parse_json(AuthenticationInfo).principalEmail), "na"),
GCPUserIp = tostring(parse_json(RequestMetadata).callerIp),
GCPCallerUA = tostring(parse_json(RequestMetadata).callerSuppliedUserAgent)
// Filter out empty or service account identities
| where isnotempty(GCPUserIdentity) and GCPUserIdentity !endswith "gserviceaccount.com"
// Select relevant attributes for further analysis
| project
PrincipalEmail,
GCPUserIdentity,
GCPAccoutType,
GCPRequestName,
GCPCallerUA,
Request,
RequestMetadata,
GCPUserIp,
MethodName,
ServiceName,
GCPEventTime= TimeGenerated,
ProjectId
// Join GCP Audit Logs with SecurityAlert data based on user identity and IP
| join kind=inner (
SecurityAlert
// Exclude alerts from Azure Sentinel
| where ProductName !in ("Azure Sentinel")
// Extract IP entities from alert data
| extend AlertIPEntity= tostring(extract(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", 0, Entities))
| extend
AlertUserUPN = tostring(extract(@'\b[\w\.\-]+@[\w\.\-]+\b', 0, Entities)),
AlertTime= TimeGenerated
// Filter out empty user identities and IP entities
| where isnotempty(AlertIPEntity) and isnotempty(AlertUserUPN)
)
on $left.GCPUserIdentity == $right.AlertUserUPN and $left.GCPUserIp == $right.AlertIPEntity
// Summarize the data, calculating time differences and aggregating attributes
| summarize
FirstAlert=min(AlertTime),
LastAlert=max(AlertTime),
TimeDiff=datetime_diff('minute', min(AlertTime), min(GCPEventTime)),
MethodName=make_set(MethodName),
ServiceName= make_set(ServiceName),
GCPProjctId=make_set(ProjectId),
Request=make_set(Request),
GCPCallerUA=make_set(GCPCallerUA)
by
AlertUserUPN,
AlertIPEntity,
GCPUserIp,
GCPUserIdentity,
AlertSeverity,
AlertName,
AlertLink,
Description,
Tactics,
ProductName,
SystemAlertId,
GCPAccoutType
// Extend the data with additional attributes
| extend
Name = tostring(split(GCPUserIdentity, "@")[0]),
UPNSuffix = tostring(split(GCPUserIdentity, "@")[1])
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: GCPUserIp
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: GCPUserIdentity
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
customDetails:
AlertName: AlertName
FirstAlert: FirstAlert
LastAlert: LastAlert
TimeDiff: TimeDiff
MethodName: MethodName
GCPProjctId: GCPProjctId
GCPCallerUA: GCPCallerUA
ServiceName: ServiceName
AlertUserUPN: AlertUserUPN
SystemAlertId: SystemAlertId
Tactics: Tactics
Request: Request
CorrelationWith: "GCPAuditLogs"
alertDetailsOverride:
alertDisplayNameFormat: "A user {{GCPUserUPN}} has been linked to {{AlertName}}, and has potentially suspicious behavior within the GCP environment from, originating from the IP address {{GCPUserIp}}."
alertDescriptionFormat: " This detection compiles and correlates unauthorized user access alerts originating from {{ProductName}} With Alert Description '{{Description}}' observed activity in GCP environmeny. It focuses on Microsoft Security, specifically targeting user bhaviour and network IP associations tied to activities such as logins from malicious IP addresses or instance credential exfiltration attempts. The detection leverages these common network IP advisories to detect and pinpoint users suspicious activity to access both Azure and GCP resources. \n\n Microsoft Security ALert Link : '{{AlertLink}}'"
alertSeverityColumnName: AlertSeverity
alertDynamicProperties:
- alertProperty: AlertLink
value: AlertLink
- alertProperty: ProviderName
value: "ProductName"
- alertProperty: ProductComponentName
value: "Microsoft Security"
kind: Scheduled
version: 1.0.2
Stages and Predicates
Stage 1: source
GCPAuditLogs
Stage 2: where
| where PrincipalEmail !endswith "gserviceaccount.com"
Stage 3: where
| where AuthenticationInfo !has ("system:")
Stage 4: extend
| extend GCPRequestName= parse_json(Request).name
Stage 5: extend
| extend
GCPAccoutType= tostring(split(GCPRequestName, "/")[2]),
GCPUserIdentity = iff(isempty(tostring(split(GCPRequestName, "/")[3])), tostring(parse_json(AuthenticationInfo).principalEmail), "na"),
GCPUserIp = tostring(parse_json(RequestMetadata).callerIp),
GCPCallerUA = tostring(parse_json(RequestMetadata).callerSuppliedUserAgent)
GCPUserIdentity =/* macro: isempty(tostring(split(GCPRequestName, "/")[3])) */tostring(parse_json(AuthenticationInfo).principalEmail)"na"Stage 6: where
| where isnotempty(GCPUserIdentity) and GCPUserIdentity !endswith "gserviceaccount.com"
Stage 7: project
| project
PrincipalEmail,
GCPUserIdentity,
GCPAccoutType,
GCPRequestName,
GCPCallerUA,
Request,
RequestMetadata,
GCPUserIp,
MethodName,
ServiceName,
GCPEventTime= TimeGenerated,
ProjectId
Stage 8: join
| join kind=inner (
SecurityAlert
| where ProductName !in ("Azure Sentinel")
| extend AlertIPEntity= tostring(extract(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", 0, Entities))
| extend
AlertUserUPN = tostring(extract(@'\b[\w\.\-]+@[\w\.\-]+\b', 0, Entities)),
AlertTime= TimeGenerated
| where isnotempty(AlertIPEntity) and isnotempty(AlertUserUPN)
)
on $left.GCPUserIdentity == $right.AlertUserUPN and $left.GCPUserIp == $right.AlertIPEntity
Stage 9: summarize
summarize FirstAlert, GCPCallerUA, GCPProjctId, LastAlert, MethodName, Request, ServiceName, TimeDiff by AlertUserUPN, AlertIPEntity, GCPUserIp, GCPUserIdentity, AlertSeverity, AlertName, AlertLink, Description, Tactics, ProductName, SystemAlertId, GCPAccoutType
Stage 10: extend
extend Name, UPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AlertIPEntity | is_not_null | field:"AlertIPEntity" kind:is_not_null | |
AlertUserUPN | is_not_null | field:"AlertUserUPN" kind:is_not_null | |
GCPUserIdentity | is_not_null | field:"GCPUserIdentity" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
PrincipalEmail | ends_with | gserviceaccount.com | excludes:PrincipalEmail field:"PrincipalEmail" value:"gserviceaccount.com" |
AuthenticationInfo | match | system: | excludes:AuthenticationInfo field:"AuthenticationInfo" value:"system:" |
GCPUserIdentity | ends_with | gserviceaccount.com | excludes:GCPUserIdentity field:"GCPUserIdentity" value:"gserviceaccount.com" |
ProductName | eq | Azure Sentinel | excludes:ProductName field:"ProductName" value:"Azure Sentinel" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertIPEntity | summarize |
AlertLink | summarize |
AlertName | summarize |
AlertSeverity | summarize |
AlertUserUPN | summarize |
Description | summarize |
FirstAlert | summarize |
GCPAccoutType | summarize |
GCPCallerUA | summarize |
GCPProjctId | summarize |
GCPUserIdentity | summarize |
GCPUserIp | summarize |
LastAlert | summarize |
MethodName | summarize |
ProductName | summarize |
Request | summarize |
ServiceName | summarize |
SystemAlertId | summarize |
Tactics | summarize |
TimeDiff | summarize |
Name | extend |
UPNSuffix | extend |
Cyble Vision Alerts Compromised Files
#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 compromised files containing credential or logon data (stealer logs) related to monitored entities. Uses Alerts_compromised_files parser to expose file paths, log objects, and extracted email identifiers.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Exfiltration |
Rule Body
id: 0f6a8287-09ee-4f82-b8c3-e35c4ac6212e
name: Cyble Vision Alerts Compromised Files
description: |
'Detects compromised files containing credential or logon data (stealer logs) related to monitored entities. Uses Alerts_compromised_files parser to expose file paths, log objects, and extracted email identifiers.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- CredentialAccess
- Exfiltration
relevantTechniques:
- T1552
- T1041
query: |
Alerts_compromised_files
| where Service == "compromised_files"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: Compromised Credentials File {{CF_Filename}}
alertDescriptionFormat: |
A compromised file containing potential credential or logon data was discovered. Relative path {{CF_RelativePath}}. Log object path {{CF_LogObjPath}}. Primary email (if found) {{CF_PrimaryEmail}}.
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
CF_Filename: CF_Filename
CF_FileObjPath: CF_FileObjPath
CF_LogObjPath: CF_LogObjPath
CF_PrimaryEmail: CF_PrimaryEmail
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CF_PrimaryEmail
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_compromised_files
Stage 2: where
| where Service == "compromised_files"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"compromised_files" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
CybleVision Alerts Cyber Crime Forum 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 discussions, marketplace posts, threat actor activity, and intelligence mentions from cybercrime forums. Extracts discussion metadata, topic details, URLs, emails, phone numbers, and content using Alerts_CyberCrimeForums parser.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Resource Development | |
| Exfiltration |
Rule Body
id: b78c4641-cc16-48e0-9d05-c9b36a55d214
name: CybleVision Alerts Cyber Crime Forum Alerts
description: |
'Detects discussions, marketplace posts, threat actor activity, and intelligence mentions from cybercrime forums. Extracts discussion metadata, topic details, URLs, emails, phone numbers, and content using Alerts_CyberCrimeForums parser.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- Reconnaissance
- ResourceDevelopment
- Exfiltration
relevantTechniques:
- T1598
- T1585
- T1589
- T1646
subTechniques: []
query: |
Alerts_cyber_crime_forums
| where Service == "cyber_crime_forums"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: CybleVision Forum Mention {{CF_TopicName}}
alertDescriptionFormat: |
A mention was detected on a cybercrime forum. Source {{CF_SourceName}} By {{CF_DiscussionBy}} Content {{CF_Content}}
alertDynamicProperties: []
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
TopicName: CF_TopicName
DiscussionBy: CF_DiscussionBy
DiscussionDate: CF_DiscussionDate
Content: CF_Content
Phones: CF_Phones
TopicURL: CF_TopicURL
TopicID: CF_TopicID
TopicCreatedBy: CF_TopicCreatedBy
Reputation: CF_Reputation
Likes: CF_Likes
Tags: CF_Tags
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: CF_Emails
- entityType: URL
fieldMappings:
- identifier: Url
columnName: CF_Urls
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: CF_SourceName
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_cyber_crime_forums
Stage 2: where
| where Service == "cyber_crime_forums"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"cyber_crime_forums" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
Digital Guardian - Unexpected protocol
#'Detects RDP protocol usage for data transfer which is not common.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: a14f2f95-bbd2-4036-ad59-e3aff132b296
name: Digital Guardian - Unexpected protocol
description: |
'Detects RDP protocol usage for data transfer which is not common.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1048
query: |
DigitalGuardianDLPEvent
| where DstPortNumber == 3389
| extend AccountCustomEntity = SrcUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
DigitalGuardianDLPEvent
Stage 2: where
| where DstPortNumber == 3389
Stage 3: extend
| extend AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstPortNumber | eq |
| field:"DstPortNumber" kind:eq value:"3389" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
Jira - User's password changed multiple times
#'Detects when user's password was changed multiple times from different IP addresses.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: 943176e8-b979-45c0-8ad3-58ba6cfd41f0
name: Jira - User's password changed multiple times
description: |
'Detects when user's password was changed multiple times from different IP addresses.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: JiraAuditAPI
dataTypes:
- JiraAudit
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1078
query: |
JiraAudit
| where EventMessage =~ "User's password changed"
| extend user = todynamic(AssociatedItems)[0]['name']
| summarize ip_list = makeset(SrcIpAddr) by tostring(user), bin(TimeGenerated, 30m)
| where array_length(ip_list) > 1
| extend AccountCustomEntity = user, IPCustomEntity = ip_list
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
JiraAudit
Stage 2: where
| where EventMessage =~ "User's password changed"
Stage 3: extend
| extend user = todynamic(AssociatedItems)[0]['name']
Stage 4: summarize
| summarize ip_list = makeset(SrcIpAddr) by tostring(user), bin(TimeGenerated, 30m)
Stage 5: where
| where array_length(ip_list) > 1
Stage 6: extend
| extend AccountCustomEntity = user, IPCustomEntity = ip_list
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | eq |
| field:"EventMessage" kind:eq value:"User's password changed" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ip_list | summarize |
AccountCustomEntity | extend |
IPCustomEntity | extend |
Malware attachment delivered
#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.
'This query identifies a message containing a malware attachment that was delivered.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 0558155e-4556-447e-9a22-828f2a7de06b
name: Malware attachment delivered
description: |
'This query identifies a message containing a malware attachment that was delivered.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ProofpointTAPv2
dataTypes:
- ProofPointTAPMessagesDeliveredV2_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566.001
query: |
ProofPointTAPMessagesDeliveredV2_CL
| mv-expand todynamic(threatsInfoMap)
| mv-expand todynamic(messageParts)
| extend threatType = tostring(threatsInfoMap.threatType), classification = tostring(threatsInfoMap.classification)
| extend filename = tostring(messageParts.filename)
| where threatType =~ "attachment" and classification =~ "malware"
| summarize filenames = make_set(filename), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by TimeGenerated, Sender = sender, SenderIPAddress = senderIP, Recipient = tostring(recipient), threatType, classification, Subject = subject
| mv-expand todynamic(Recipient)
| extend RecipientName = tostring(split(Recipient, "@")[0]), RecipientUPNSuffix = tostring(split(Recipient, "@")[1])
| extend SenderName = tostring(split(Sender, "@")[0]), SenderUPNSuffix = tostring(split(Sender, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Recipient
- identifier: Name
columnName: RecipientName
- identifier: UPNSuffix
columnName: RecipientUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Sender
- identifier: Name
columnName: SenderName
- identifier: UPNSuffix
columnName: SenderUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SenderIPAddress
version: 1.0.6
kind: Scheduled
Stages and Predicates
Stage 1: source
ProofPointTAPMessagesDeliveredV2_CL
Stage 2: mv-expand
| mv-expand todynamic(threatsInfoMap)
Stage 3: mv-expand
| mv-expand todynamic(messageParts)
Stage 4: extend
| extend threatType = tostring(threatsInfoMap.threatType), classification = tostring(threatsInfoMap.classification)
Stage 5: extend
| extend filename = tostring(messageParts.filename)
Stage 6: where
| where threatType =~ "attachment" and classification =~ "malware"
Stage 7: summarize
| summarize filenames = make_set(filename), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by TimeGenerated, Sender = sender, SenderIPAddress = senderIP, Recipient = tostring(recipient), threatType, classification, Subject = subject
Stage 8: mv-expand
| mv-expand todynamic(Recipient)
Stage 9: extend
| extend RecipientName = tostring(split(Recipient, "@")[0]), RecipientUPNSuffix = tostring(split(Recipient, "@")[1])
Stage 10: extend
| extend SenderName = tostring(split(Sender, "@")[0]), SenderUPNSuffix = tostring(split(Sender, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
classification | eq |
| field:"classification" kind:eq value:"malware" |
threatType | eq |
| field:"threatType" kind:eq value:"attachment" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EndTime | summarize |
Recipient | summarize |
Sender | summarize |
SenderIPAddress | summarize |
StartTime | summarize |
Subject | summarize |
TimeGenerated | summarize |
classification | summarize |
filenames | summarize |
threatType | summarize |
RecipientName | extend |
RecipientUPNSuffix | extend |
SenderName | extend |
SenderUPNSuffix | extend |
Mimecast Secure Email Gateway - Impersonation Protect
#'Detects threats from impersonation mail under targeted threat protection.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 2ef77cef-439f-4d94-848f-3eca67510d2f
name: Mimecast Secure Email Gateway - Impersonation Protect
description: |
'Detects threats from impersonation mail under targeted threat protection.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: MimecastSEGAPI
dataTypes:
- MimecastCG
enabled: true
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- LateralMovement
- Collection
relevantTechniques:
- T1114
query: |
MimecastCG
| where Type == "email_ttp_impersonation"
| extend SenderEnvelope = ['Sender Envelope'] , SenderIp = ['Sender IP']
suppressionDuration: 5h
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P7D
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: SenderEnvelope
- identifier: SenderIP
columnName: SenderIp
- identifier: Recipient
columnName: Recipients
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastCG
Stage 2: where
| where Type == "email_ttp_impersonation"
Stage 3: extend
| extend SenderEnvelope = ['Sender Envelope'] , SenderIp = ['Sender IP']
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Type | eq |
| field:"Type" kind:eq value:"email_ttp_impersonation" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SenderEnvelope | extend |
SenderIp | extend |
Mimecast Secure Email Gateway - Impersonation Protect
#Detects threats from impersonation mail under targeted threat protection
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 7034abc9-6b66-4533-9bf3-056672fd9d9e
name: Mimecast Secure Email Gateway - Impersonation Protect
description: Detects threats from impersonation mail under targeted threat protection
severity: High
requiredDataConnectors:
- connectorId: MimecastSIEMAPI
dataTypes:
- MimecastSIEM_CL
enabled: true
query: MimecastSIEM_CL| where mimecastEventId_s == "mail_ttp_impersonation"
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Discovery
- LateralMovement
- Collection
relevantTechniques:
- T1114
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
Subject: Subject_s
MsgId_s: MsgId_s
Route: Route_s
CustomThreatDict: CustomThreatDictionary_s
Action: Action_s
Hits: Hits_s
SimilarCustExtDomain: SimilarCustomExternalDomain_s
TaggedExternal: TaggedExternal_s
SimilarIntDomain: SimilarInternalDomain_s
Definition: Definition_s
NewDomain: NewDomain_s
InternalName: InternalName_s
ThreatDictionary: ThreatDictionary_s
SimilarMCExtDomain: SimilarMimecastExternalDomain_s
CustomName: CustomName_s
TaggedMalicious: TaggedMalicious_s
ReplyMismatch: ReplyMismatch_s
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: Sender_s
- identifier: SenderIP
columnName: IP_s
- identifier: Recipient
columnName: Recipient_s
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastSIEM_CL
Stage 2: where
| where mimecastEventId_s == "mail_ttp_impersonation"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
mimecastEventId_s | eq |
| field:"mimecastEventId_s" kind:eq value:"mail_ttp_impersonation" |
Multiple Teams deleted by a single user
#This detection flags the occurrences of deleting multiple teams within an hour. This data is a part of Office 365 Connector in Microsoft Sentinel.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | TeamDeleted |
Rule Body
id: 173f8699-6af5-484a-8b06-8c47ba89b380
name: Multiple Teams deleted by a single user
description: |
'This detection flags the occurrences of deleting multiple teams within an hour.
This data is a part of Office 365 Connector in Microsoft Sentinel.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity (Teams)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
- T1489
query: |
// Adjust this value to change how many Teams should be deleted before including
let max_delete_count = 3;
// Adjust this value to change the timewindow the query runs over
OfficeActivity
| where OfficeWorkload =~ "MicrosoftTeams"
| where Operation =~ "TeamDeleted"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DeletedTeams = make_set(TeamName, 1000) by UserId
| where array_length(DeletedTeams) > max_delete_count
| extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
version: 2.0.4
kind: Scheduled
Stages and Predicates
Parameters
let max_delete_count = 3;
Stage 1: source
OfficeActivity
Stage 2: where
| where OfficeWorkload =~ "MicrosoftTeams"
Stage 3: where
| where Operation =~ "TeamDeleted"
Stage 4: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), DeletedTeams = make_set(TeamName, 1000) by UserId
Stage 5: where
| where array_length(DeletedTeams) > max_delete_count
Stage 6: extend
| extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OfficeWorkload | eq |
| field:"m365::Workload" kind:eq value:"MicrosoftTeams" |
Operation | eq |
| field:"Operation" kind:eq value:"TeamDeleted" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeletedTeams | summarize |
EndTime | summarize |
StartTime | summarize |
UserId | summarize |
AccountName | extend |
AccountUPNSuffix | extend |
NRT Squid proxy events related to mining pools
#'Checks for Squid proxy events in Syslog associated with common mining pools .This query presumes the default Squid log format is being used. http://www.squid-cache.org/Doc/config/access_log/'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: dd03057e-4347-4853-bf1e-2b2d21eb4e59
name: NRT Squid proxy events related to mining pools
description: |
'Checks for Squid proxy events in Syslog associated with common mining pools .This query presumes the default Squid log format is being used.
http://www.squid-cache.org/Doc/config/access_log/'
severity: Low
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
tactics:
- CommandAndControl
relevantTechniques:
- T1102
query: |
let DomainList = dynamic(["monerohash.com", "do-dear.com", "xmrminerpro.com", "secumine.net", "xmrpool.com", "minexmr.org", "hashanywhere.com", "xmrget.com",
"mininglottery.eu", "minergate.com", "moriaxmr.com", "multipooler.com", "moneropools.com", "xmrpool.eu", "coolmining.club", "supportxmr.com",
"minexmr.com", "hashvault.pro", "xmrpool.net", "crypto-pool.fr", "xmr.pt", "miner.rocks", "walpool.com", "herominers.com", "gntl.co.uk", "semipool.com",
"coinfoundry.org", "cryptoknight.cc", "fairhash.org", "baikalmine.com", "tubepool.xyz", "fairpool.xyz", "asiapool.io", "coinpoolit.webhop.me", "nanopool.org",
"moneropool.com", "miner.center", "prohash.net", "poolto.be", "cryptoescrow.eu", "monerominers.net", "cryptonotepool.org", "extrmepool.org", "webcoin.me",
"kippo.eu", "hashinvest.ws", "monero.farm", "supportxmr.com", "xmrpool.eu", "linux-repository-updates.com", "1gh.com", "dwarfpool.com", "hash-to-coins.com",
"hashvault.pro", "pool-proxy.com", "hashfor.cash", "fairpool.cloud", "litecoinpool.org", "mineshaft.ml", "abcxyz.stream", "moneropool.ru", "cryptonotepool.org.uk",
"extremepool.org", "extremehash.com", "hashinvest.net", "unipool.pro", "crypto-pools.org", "monero.net", "backup-pool.com", "mooo.com", "freeyy.me", "cryptonight.net",
"shscrypto.net"]);
Syslog
| where ProcessName contains "squid"
| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
HTTP_Status_Code = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))/([0-9]{3})",8,SyslogMessage),
User = extract("(CONNECT |GET )([^ ]* )([^ ]+)",3,SyslogMessage),
RemotePort = extract("(CONNECT |GET )([^ ]*)(:)([0-9]*)",4,SyslogMessage),
Domain = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :\\/]*)",3,SyslogMessage),
Bytes = toint(extract("([A-Z]+\\/[0-9]{3} )([0-9]+)",2,SyslogMessage)),
contentType = extract("([a-z/]+$)",1,SyslogMessage)
| extend TLD = extract("\\.[a-z]*$",0,Domain)
| where HTTP_Status_Code == '200'
| where Domain contains "."
| where Domain has_any (DomainList)
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: User
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
version: 1.0.1
kind: NRT
Stages and Predicates
Let Binding: DomainList
let DomainList = dynamic(["monerohash.com", "do-dear.com", "xmrminerpro.com", "secumine.net", "xmrpool.com", "minexmr.org", "hashanywhere.com", "xmrget.com",
"mininglottery.eu", "minergate.com", "moriaxmr.com", "multipooler.com", "moneropools.com", "xmrpool.eu", "coolmining.club", "supportxmr.com",
"minexmr.com", "hashvault.pro", "xmrpool.net", "crypto-pool.fr", "xmr.pt", "miner.rocks", "walpool.com", "herominers.com", "gntl.co.uk", "semipool.com",
"coinfoundry.org", "cryptoknight.cc", "fairhash.org", "baikalmine.com", "tubepool.xyz", "fairpool.xyz", "asiapool.io", "coinpoolit.webhop.me", "nanopool.org",
"moneropool.com", "miner.center", "prohash.net", "poolto.be", "cryptoescrow.eu", "monerominers.net", "cryptonotepool.org", "extrmepool.org", "webcoin.me",
"kippo.eu", "hashinvest.ws", "monero.farm", "supportxmr.com", "xmrpool.eu", "linux-repository-updates.com", "1gh.com", "dwarfpool.com", "hash-to-coins.com",
"hashvault.pro", "pool-proxy.com", "hashfor.cash", "fairpool.cloud", "litecoinpool.org", "mineshaft.ml", "abcxyz.stream", "moneropool.ru", "cryptonotepool.org.uk",
"extremepool.org", "extremehash.com", "hashinvest.net", "unipool.pro", "crypto-pools.org", "monero.net", "backup-pool.com", "mooo.com", "freeyy.me", "cryptonight.net",
"shscrypto.net"]);
Stage 1: source
Syslog
Stage 2: where
| where ProcessName contains "squid"
Stage 3: extend
| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
HTTP_Status_Code = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))/([0-9]{3})",8,SyslogMessage),
User = extract("(CONNECT |GET )([^ ]* )([^ ]+)",3,SyslogMessage),
RemotePort = extract("(CONNECT |GET )([^ ]*)(:)([0-9]*)",4,SyslogMessage),
Domain = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :\\/]*)",3,SyslogMessage),
Bytes = toint(extract("([A-Z]+\\/[0-9]{3} )([0-9]+)",2,SyslogMessage)),
contentType = extract("([a-z/]+$)",1,SyslogMessage)
Stage 4: extend
| extend TLD = extract("\\.[a-z]*$",0,Domain)
Stage 5: where
| where HTTP_Status_Code == '200'
Stage 6: where
| where Domain contains "."
Stage 7: where
| where Domain has_any (DomainList)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Domain | contains |
| field:"Domain" kind:contains value:"." |
Domain | match |
| field:"Domain" kind:match |
HTTP_Status_Code | eq |
| field:"HTTP_Status_Code" kind:eq value:"200" |
ProcessName | contains |
| field:"process_name" kind:contains value:"squid" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Bytes | extend |
Domain | extend |
HTTP_Status_Code | extend |
RemotePort | extend |
SourceIP | extend |
Status | extend |
URL | extend |
User | extend |
contentType | extend |
TLD | extend |
Process executed from binary hidden in Base64 encoded file
#Encoding malicious software is a technique used to obfuscate files from detection. The first CommandLine component is looking for Python decoding base64. The second CommandLine component is looking for Bash/sh command line base64 decoding. The third one is looking for Ruby decoding base64.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
id: d6190dde-8fd2-456a-ac5b-0a32400b0464
name: Process executed from binary hidden in Base64 encoded file
description: |
'Encoding malicious software is a technique used to obfuscate files from detection.
The first CommandLine component is looking for Python decoding base64.
The second CommandLine component is looking for Bash/sh command line base64 decoding.
The third one is looking for Ruby decoding base64.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- DefenseEvasion
relevantTechniques:
- T1059
- T1027
- T1140
query: |
let ProcessCreationEvents=(union isfuzzy=true
(SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, FileName = Process, CommandLine, ParentProcessName
),
(WindowsEvent
| where EventID==4688
| where EventData has_any (".decode('base64')", "base64 --decode", ".decode64(" )
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend FileName=tostring(split(NewProcessName, '\\')[-1])
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, CommandLine, ParentProcessName
));
ProcessCreationEvents
| where CommandLine contains ".decode('base64')"
or CommandLine contains "base64 --decode"
or CommandLine contains ".decode64("
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), CountToday = count() by Computer, Account, AccountDomain, FileName, CommandLine, ParentProcessName
| extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Account
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.1.4
kind: Scheduled
Stages and Predicates
Stage 0: let
let ProcessCreationEvents = (union <inlined as stages below>;
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, WindowsEvent
Leg 1: SecurityEvent
SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, FileName = Process, CommandLine, ParentProcessName
Leg 2: WindowsEvent
WindowsEvent
| where EventID==4688
| where EventData has_any (".decode('base64')", "base64 --decode", ".decode64(" )
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend FileName=tostring(split(NewProcessName, '\\')[-1])
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, CommandLine, ParentProcessName
Applied to the Combined Result
| where CommandLine contains ".decode('base64')"
or CommandLine contains "base64 --decode"
or CommandLine contains ".decode64(" | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), CountToday = count() by Computer, Account, AccountDomain, FileName, CommandLine, ParentProcessName | extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | contains |
| field:"CommandLine" kind:contains |
CommandLine | is_not_null | field:"CommandLine" kind:is_not_null | |
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | summarize |
AccountDomain | summarize |
CommandLine | summarize |
Computer | summarize |
CountToday | summarize |
EndTimeUtc | summarize |
FileName | summarize |
ParentProcessName | summarize |
StartTimeUtc | summarize |
DnsDomain | extend |
HostName | extend |
Tape Media Vault Deleted
#Detects when a tape media vault is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.
Rule Body
id: 19ed623e-1b1c-45e0-ac74-c1e629fbf117
name: Tape Media Vault Deleted
description: Detects when a tape media vault is deleted from Veeam Backup & Replication.
This might indicate unauthorized removal of critical components.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 23631\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n [\"User Name\"] = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 23631
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
["User Name"] = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"23631" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
User Name | project |
Tenable.ad Password Guessing
#'Searches for bruteforce Password Guessing attacks.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: 44d74560-0cd1-4e73-a8f5-d16eeeba219e
name: Tenable.ad Password Guessing
description: |
'Searches for bruteforce Password Guessing attacks.'
severity: High
requiredDataConnectors:
- connectorId: Tenable.ad
dataTypes:
- Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
// For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
// Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
// Then, create the Kusto Function with alias afad_parser
afad_parser
| where MessageType == 2 and Codename == "Password Guessing"
| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
afad_parser
Stage 2: where
| where MessageType == 2 and Codename == "Password Guessing"
Stage 3: extend
| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Codename | eq |
| field:"Codename" kind:eq value:"Password Guessing" |
MessageType | eq |
| field:"MessageType" kind:eq value:"2" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
TI map IP entity to AzureFirewall
#Identifies a match in AzureFirewall (NetworkRule & ApplicationRule Logs) from any IP IOC from TI
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 0b904747-1336-4363-8d84-df2710bfe5e7
name: TI map IP entity to AzureFirewall
description: |
Identifies a match in AzureFirewall (NetworkRule & ApplicationRule Logs) from any IP IOC from TI
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h; // Look back 1 hour for AzureDiagnostics logs
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = ThreatIntelligenceIndicator
// Filter out indicators without relevant IP address fields
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| where TimeGenerated >= ago(ioc_lookBack)
// Select the IP entity based on availability of different IP fields
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
// Exclude local addresses using the ipv4_is_private operator and filtering out specific address prefixes
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now();
// Perform a join between IP indicators and AzureDiagnostics logs to identify potential malicious activity
IP_Indicators
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (
AzureDiagnostics
| where TimeGenerated >= ago(dt_lookBack)
| where OperationName in ("AzureFirewallApplicationRuleLog", "AzureFirewallNetworkRuleLog")
| parse kind=regex flags=U msg_s with Protocol 'request from ' SourceHost 'to ' DestinationHost @'\.? Action: ' Firewall_Action @'\.' Rest_msg
| extend SourceAddress = extract(@'([\.0-9]+)(:[\.0-9]+)?', 1, SourceHost)
| extend DestinationAddress = extract(@'([\.0-9]+)(:[\.0-9]+)?', 1, DestinationHost)
| extend RemoteIP = case(not(ipv4_is_private(DestinationAddress)), DestinationAddress, not(ipv4_is_private(SourceAddress)), SourceAddress, "")
| where isnotempty(RemoteIP) // Filter out traffic involving public addresses only
| project-rename AzureFirewall_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.RemoteIP
// Filter out logs that occurred after the expiration of the corresponding indicator
| where AzureFirewall_TimeGenerated < ExpirationDateTime
// Group the results by IndicatorId and RemoteIP, and keep the log entry with the latest timestamp
| summarize AzureFirewall_TimeGenerated = arg_max(AzureFirewall_TimeGenerated, *) by IndicatorId, RemoteIP
// Select the desired output fields
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, DomainName, ExpirationDateTime, ConfidenceScore,
AzureFirewall_TimeGenerated, TI_ipEntity, Resource, Category, msg_s, SourceAddress, DestinationAddress, Firewall_Action, Protocol,
NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, Type
// Rename the timestamp field
| extend timestamp = AzureFirewall_TimeGenerated
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: TI_ipEntity
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.3.2
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 7 define let IP_Indicators as the rule's main pipeline source. Stages 8 to 12 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 3: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 4: extend (3 consecutive steps)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =isnotempty(NetworkIP)NetworkIPNetworkDestinationIPStage 5: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 6: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 7: where
| where Active == true and ExpirationDateTime > now()
Stage 8: join
IP_Indicators
| join kind=innerunique (
AzureDiagnostics
| where TimeGenerated >= ago(dt_lookBack)
| where OperationName in ("AzureFirewallApplicationRuleLog", "AzureFirewallNetworkRuleLog")
| parse kind=regex flags=U msg_s with Protocol 'request from ' SourceHost 'to ' DestinationHost @'\.? Action: ' Firewall_Action @'\.' Rest_msg
| extend SourceAddress = extract(@'([\.0-9]+)(:[\.0-9]+)?', 1, SourceHost)
| extend DestinationAddress = extract(@'([\.0-9]+)(:[\.0-9]+)?', 1, DestinationHost)
| extend RemoteIP = case(not(ipv4_is_private(DestinationAddress)), DestinationAddress, not(ipv4_is_private(SourceAddress)), SourceAddress, "")
| where isnotempty(RemoteIP)
| project-rename AzureFirewall_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.RemoteIP
Stage 9: where
where AzureFirewall_TimeGenerated < ExpirationDateTime
Stage 10: summarize
summarize AzureFirewall_TimeGenerated by IndicatorId, RemoteIP
Stage 11: project
project ActivityGroupNames, AzureFirewall_TimeGenerated, Category, ConfidenceScore, Description, DestinationAddress, DomainName, EmailSourceIpAddress, ExpirationDateTime, Firewall_Action, IndicatorId, LatestIndicatorTime, NetworkDestinationIP, NetworkIP, NetworkSourceIP, Protocol, Resource, SourceAddress, TI_ipEntity, ThreatType, Type, Url, msg_s
Stage 12: extend
extend timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
EmailSourceIpAddress | is_not_null | field:"EmailSourceIpAddress" kind:is_not_null | |
NetworkDestinationIP | is_not_null | field:"NetworkDestinationIP" kind:is_not_null | |
NetworkIP | is_not_null | field:"NetworkIP" kind:is_not_null | |
NetworkSourceIP | is_not_null | field:"NetworkSourceIP" kind:is_not_null | |
OperationName | in |
| field:"OperationName" kind:in |
RemoteIP | is_not_null | field:"RemoteIP" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TI_ipEntity | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:TI_ipEntity |
TI_ipEntity | starts_with | 127. | excludes:TI_ipEntity field:"TI_ipEntity" value:"127." |
TI_ipEntity | starts_with | :: | excludes:TI_ipEntity field:"TI_ipEntity" value:"::" |
TI_ipEntity | starts_with | fe80 | excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
AzureFirewall_TimeGenerated | project |
Category | project |
ConfidenceScore | project |
Description | project |
DestinationAddress | project |
DomainName | project |
EmailSourceIpAddress | project |
ExpirationDateTime | project |
Firewall_Action | project |
IndicatorId | project |
LatestIndicatorTime | project |
NetworkDestinationIP | project |
NetworkIP | project |
NetworkSourceIP | project |
Protocol | project |
Resource | project |
SourceAddress | project |
TI_ipEntity | project |
ThreatType | project |
Type | project |
Url | project |
msg_s | project |
timestamp | extend |
Whisper Security - Co-Hosted Malware Cluster Detection
#Identifies IP addresses that host multiple domains associated with malware. When an IP has more than 3 co-hosted domains and at least 3 of those domains are flagged as malware by Whisper threat intelligence, it strongly indicates compromised or adversary-controlled infrastructure.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Resource Development |
Rule Body
id: 374a77f4-23ed-55dc-8441-8e47a1e079e9
name: Whisper Security - Co-Hosted Malware Cluster Detection
description: |
Identifies IP addresses that host multiple domains associated with malware. When an IP has more than 3 co-hosted domains and at least 3 of those domains are flagged as malware by Whisper threat intelligence, it strongly indicates compromised or adversary-controlled infrastructure.
severity: High
status: Available
requiredDataConnectors:
- connectorId: WhisperSecurityConnector
dataTypes:
- WhisperInfraContext_CL
- WhisperThreatIntel_CL
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- ResourceDevelopment
relevantTechniques:
- T1584
query: |
// MITRE ATT&CK: T1584.001 - Compromise Infrastructure: Domains
// Tactic: Resource Development
// Detects clusters of malware-hosting domains sharing the same IP infrastructure
let minCohostedCount = 3;
let minMalwareCoHosts = 3;
let cohostedInfra = WhisperInfraContext_CL
| where TimeGenerated > ago(1d)
| where cohostedCount > minCohostedCount
| extend parsedIp = tostring(split(ipAddresses, ",")[0])
| project indicator, parsedIp, cohostedCount, countries, registrar;
let malwareIndicators = WhisperThreatIntel_CL
| where TimeGenerated > ago(1d)
| where isMalware == true
| project indicator, threatScore, threatLevel, feedNames;
cohostedInfra
| join kind=inner (malwareIndicators) on indicator
| summarize malwareCoHostCount = dcount(indicator), DnsDomains = make_set(indicator, 10), avgThreatScore = avg(threatScore), maxThreatScore = max(threatScore) by parsedIp, cohostedCount, countries, registrar
| where malwareCoHostCount >= minMalwareCoHosts
| extend DnsDomain = tostring(DnsDomains[0]), IPAddress = parsedIp
| project TimeGenerated = now(), DnsDomain, IPAddress, malwareCoHostCount, cohostedCount, avgThreatScore, maxThreatScore, countries, registrar, DnsDomains
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
customDetails:
MalwareCoHostCount: malwareCoHostCount
CoHostedCount: cohostedCount
MaxThreatScore: maxThreatScore
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let minCohostedCount = 3;
let minMalwareCoHosts = 3;
let cohostedInfra is inlined into the numbered stages below.
Let Binding: malwareIndicators
let malwareIndicators = WhisperThreatIntel_CL
| where TimeGenerated > ago(1d)
| where isMalware == true
| project indicator, threatScore, threatLevel, feedNames;
Stages 1 to 5 define let cohostedInfra as the rule's main pipeline source. Stages 6 to 10 use it.
Stage 1: source
WhisperInfraContext_CL
Stage 2: where
| where TimeGenerated > ago(1d)
Stage 3: where
| where cohostedCount > minCohostedCount
Stage 4: extend
| extend parsedIp = tostring(split(ipAddresses, ",")[0])
Stage 5: project
| project indicator, parsedIp, cohostedCount, countries, registrar
Stage 6: join
cohostedInfra
| join kind=inner (malwareIndicators) on indicator
Stage 7: summarize
summarize DnsDomains, avgThreatScore, malwareCoHostCount, maxThreatScore by parsedIp, cohostedCount, countries, registrar
Stage 8: where
where malwareCoHostCount >= 3
Stage 9: extend
extend DnsDomain, IPAddress
Stage 10: project
project DnsDomain, DnsDomains, IPAddress, TimeGenerated, avgThreatScore, cohostedCount, countries, malwareCoHostCount, maxThreatScore, registrar
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | project |
DnsDomains | project |
IPAddress | project |
TimeGenerated | project |
avgThreatScore | project |
cohostedCount | project |
countries | project |
malwareCoHostCount | project |
maxThreatScore | project |
registrar | project |