Detection rules › Kusto
Kusto rules: 09
[Entra ID] Authentication Method Changed for Privileged Account
#Detects changes to authentication methods on privileged accounts. This may indicate an attacker added a new method to maintain access.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- Account Created and Deleted in Short Timeframe (Kusto)
- Account Created And Deleted Within A Close Time Frame (Sigma)
- Account created from non-approved sources (Kusto)
- Account created or deleted by non-approved user (Kusto)
- Addition of a Temporary Access Pass to a Privileged Account (Kusto)
- Authentication Method Changed for Privileged Account (Kusto)
- Authentication Methods Changed for Privileged Account (Kusto)
- Azure AD External Guest User Invited (Splunk)
Rule Body
id: 9f7197b6-eeb2-46f3-83b1-a2c4dfca46a0
name: "[Entra ID] Authentication Method Changed for Privileged Account"
version: 1.0.0
kind: Scheduled
description: |
Detects changes to authentication methods on privileged accounts. This may indicate an attacker added a new method to maintain access.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
query: |
let queryperiod = 1d;
let queryfrequency = 1d;
let security_info_actions = dynamic(["User registered security info", "User changed default security info", "User deleted security info", "Admin updated security info", "Admin deleted security info", "Admin registered security info"]);
let VIPUsers = (
IdentityInfo
| where TimeGenerated > ago(queryperiod)
| mv-expand AssignedRoles
| where AssignedRoles contains 'Admin'
| summarize by AccountUPN);
AuditLogs
| where TimeGenerated > ago(queryperiod)
| where Category =~ "UserManagement"
| where ActivityDisplayName in (security_info_actions)
| extend Initiator = tostring(InitiatedBy.user.userPrincipalName)
| extend IP = tostring(InitiatedBy.user.ipAddress)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingAppServicePrincipalName = tostring(InitiatedBy.app.servicePrincipalName)
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "User"
| extend Target = tostring(TargetResource.userPrincipalName)
)
| where Target in~ (VIPUsers)
// Uncomment the line below if you are experiencing high volumes of Target entities. If this is uncommented, the Target column will not be mapped to an entity.
//| summarize Start=min(TimeGenerated), End=max(TimeGenerated), Actions = make_set(ResultReason, MaxSize=8), Targets=make_set(Target, MaxSize=256) by Initiator, IP, Result
// Comment out this line below, if line above is used.
| summarize
Start=min(TimeGenerated),
End=max(TimeGenerated),
Actions = make_set(ResultReason, MaxSize=8),
Target_ResourceName = make_set(TargetResources)
by
Initiator,
IP,
Result,
Targets = Target,
InitiatingAppServicePrincipalId,
InitiatingAppServicePrincipalName,
bin(TimeGenerated, 1h)
| mv-expand Actions
| extend
Action = tostring(Actions),
InitiatorName = tostring(split(Initiator, '@', 0)[0]),
InitiatorUPNSuffix = tostring(split(Initiator, '@', 1)[0]),
//TargetName = iff(tostring(Targets) has "[", "", tostring(split(Targets, '@', 0)[0])),
TargetUPNSuffix = iff(tostring(Targets) has "[", "", tostring(split(Targets, '@', 1)[0]))
| extend Source_Network_IPLocation = ""
| project
Alert_Category_en = "Entra ID",
Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
Alert_Name_en = "Authentication Method Changed for Privileged Account",
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"
", user: ",
iff(isnotempty(Initiator), Initiator, ""),
" at IP: ",
IP,
", performed an authentication method change"
),
Alert_TriageStep_en = strcat(
"1. Check whether there are successful records for the authentication method change."
"2. Confirm with user: ",
iff(isnotempty(Initiator), Initiator, ""),
" whether the change was performed."
),
Alert_Containment_en = strcat(
"1. Immediately revoke all existing sign-in tokens and sessions (sign-in session / refresh token) for the account, and force password and MFA reset. ",
"2. Immediately disable the account."
),
Alert_Remediation_en = strcat(
"1. Strengthen security info governance for Admin/VIP accounts and require additional verification or manual review for security info changes.",
"2. Enforce advanced Conditional Access policies for high-privilege accounts, including MFA/compliant devices/risk-based sign-in policies, and block change activity from unknown locations or new devices. ",
"3. Establish and optimize real-time alerts and automated response SOAR for security info changes, such as automatically blocking accounts/notifying SOC/requesting user confirmation. "
),
Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
Alert_Time_UTC0 = TimeGenerated,
Event_Action = Actions,
Event_Status = Result,
//Event_Description = ActivityDisplayName,
Source_Identity_FullName = Initiator,
//Source_Identity_ID = ActorID,
Source_Identity_Type = iff(isnotempty(Initiator), "User", "Service"),
Source_Network_IPAddress = IP,
Source_Network_IPLocation = Source_Network_IPLocation,
Source_Resource_Name = InitiatingAppServicePrincipalName,
Target_Identity_FullName = Targets,
Target_Identity_Type = "User",
Target_Resource_Type = "Entra ID"
//CreateTime:2025/1/5
//EditTime:
//Editor:Lancy
//Version v1.0
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Source_Identity_FullName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Target_Identity_FullName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Source_Network_IPAddress
Stages and Predicates
Parameters
let queryperiod = 1d;
let queryfrequency = 1d;
Let Binding: security_info_actions
let security_info_actions = dynamic(["User registered security info", "User changed default security info", "User deleted security info", "Admin updated security info", "Admin deleted security info", "Admin registered security info"]);
Let Binding: VIPUsers
let VIPUsers = (
IdentityInfo
| where TimeGenerated > ago(queryperiod)
| mv-expand AssignedRoles
| where AssignedRoles contains 'Admin'
| summarize by AccountUPN);
Stage 1: source
let VIPUsers
Stage 2: source
AuditLogs
Stage 3: where
where TimeGenerated > ago(86400s)
Stage 4: where
where Category =~ "UserManagement"
Stage 5: where
where ActivityDisplayName in ("Admin deleted security info", "Admin registered security info", "Admin updated security info", "User changed default security info", "User deleted security info", "User registered security info")
Stage 6: extend (4 consecutive steps)
extend IP, InitiatingAppServicePrincipalId, InitiatingAppServicePrincipalName, Initiator
Stage 7: kusto:mv-apply
kusto:mv-apply TargetResource.type =~ "User"
Stage 8: where
where Target =~ "VIPUsers"
Stage 9: summarize
summarize Actions, End, Start, Target_ResourceName, Targets by Initiator, IP, Result, Targets, InitiatingAppServicePrincipalId, InitiatingAppServicePrincipalName
Stage 10: mv-expand
mv-expand Actions
Stage 11: extend
extend Action, InitiatorName, InitiatorUPNSuffix, TargetUPNSuffix
TargetUPNSuffix =Targets has "["""tostring(split(Targets, '@', 1)[0])Stage 12: extend
extend Source_Network_IPLocation
Stage 13: project
project Alert_Category_en, Alert_Containment_en, Alert_Description_en, Alert_Name_en, Alert_Remediation_en, Alert_SubCategory_en, Alert_Time_TW, Alert_Time_UTC0, Alert_TriageStep_en, Event_Action, Event_Status, Source_Identity_FullName, Source_Identity_Type, Source_Network_IPAddress, Source_Network_IPLocation, Source_Resource_Name, Target_Identity_FullName, Target_Identity_Type, Target_Resource_Type
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActivityDisplayName | in |
| field:"ActivityDisplayName" kind:in |
Category | eq |
| field:"Category" kind:eq value:"UserManagement" |
Target | in |
| field:"Target" kind:in value:"VIPUsers" |
TargetResource.type | eq |
| field:"TargetResource.type" kind:eq value:"User" |
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 |
Source_Resource_Name | project |
Target_Identity_FullName | project |
Target_Identity_Type | project |
Target_Resource_Type | project |
Cisco SE - Connection to known C2 server
#'This rule is triggered when connection to known C2 is detected from host.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 0f788a93-dc88-4f80-89ef-bef7cd0fef05
name: Cisco SE - Connection to known C2 server
description: |
'This rule is triggered when connection to known C2 is detected from host.'
severity: High
requiredDataConnectors:
- connectorId: CiscoSecureEndpoint
dataTypes:
- CiscoSecureEndpoint
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
CiscoSecureEndpoint
| where EventMessage has 'Suspected botnet connection'
| extend HostCustomEntity = DstHostname, MalwareCustomEntity = ThreatName
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostCustomEntity
- entityType: Malware
fieldMappings:
- identifier: Name
columnName: MalwareCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CiscoSecureEndpoint
Stage 2: where
| where EventMessage has 'Suspected botnet connection'
Stage 3: extend
| extend HostCustomEntity = DstHostname, MalwareCustomEntity = ThreatName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | match |
| field:"EventMessage" kind:match value:"Suspected botnet connection" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostCustomEntity | extend |
MalwareCustomEntity | extend |
Digital Shadows Incident Creation for exclude-app
#Digital Shadows Analytic rule for generating Microsoft Sentinel incidents for the data ingested by app polling for excluded classifications
Rule Body
id: f7abe9c1-1e6c-4317-b907-25769e7764c5
name: Digital Shadows Incident Creation for exclude-app
description: 'Digital Shadows Analytic rule for generating Microsoft Sentinel incidents for the data ingested by app polling for excluded classifications'
severity: Medium
requiredDataConnectors:
- connectorId: DigitalShadows
dataTypes:
- DigitalShadows_V2_CL
queryFrequency: 5m
queryPeriod: 6m
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query:
let DSSearchLight_view = view () {
DigitalShadows_V2_CL
| project-rename app_s = App, title_s = Title, raised_t = TimeRaised, updated_t = TimeUpdated, classification_s = Classification, risk_level_s = RiskLevel, risk_assessment_risk_level_s = RiskAssessmentRiskLevel, gm_link_s = GreyMatterLink, assets_s = Assets, comments_s = Comments, description_s = Description, impact_description_s = ImpactDescription, mitigation_s = Mitigation, risk_factors_s = RiskFactors, portal_id_s = PortalId, id_d = IncidentId, id_g = AlertId, status_s = Status, triage_id_g = TriageId, triage_raised_time_t = TriageRaisedTime, triage_updated_time_t = TriageUpdatedTime
| where app_s == "exclude"
| extend EventVendor="Digital Shadows", EventProduct="SearchLight",Type="DigitalShadows_V2_CL",EventStartTime=raised_t,EventMessage=title_s,EventOriginalUid=triage_id_g,EventOriginalType=classification_s
| extend EventOriginalSeverity=iif(isempty(risk_level_s), risk_assessment_risk_level_s, risk_level_s)
| extend EventSeverity = case(EventOriginalSeverity == 'none', 'Informational', EventOriginalSeverity == 'very-low', 'Low',EventOriginalSeverity == 'low', 'Low',EventOriginalSeverity == 'medium', 'Medium',EventOriginalSeverity == 'high', 'High',EventOriginalSeverity == 'very-high', 'High','Informational')
| extend EventReportUrl = column_ifexists('gm_link_s', '')
| extend AdditionalFields = pack("assets", assets_s, "comments", comments_s, "description", description_s, "incident_id", id_d, "alert_id", id_g, "short_code", portal_id_s, "impact", impact_description_s, "mitigation", mitigation_s, "risk_factors", risk_factors_s, "triage_status", status_s, "triage_id", triage_id_g, "triage_raised", triage_raised_time_t,"triage_updated", triage_updated_time_t, "updated", updated_t)
| project TimeGenerated, EventVendor,EventProduct, Type, EventStartTime,EventMessage, EventOriginalUid, EventOriginalType,EventOriginalSeverity, EventSeverity, EventReportUrl,AdditionalFields};DSSearchLight_view
| summarize arg_max(TimeGenerated, *) by EventOriginalUid
| extend description = AdditionalFields.description
| extend impact = AdditionalFields.impact
| extend mitigation = AdditionalFields.mitigation
| extend status = AdditionalFields.triage_status
| extend comments = AdditionalFields.comments
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: EventReportUrl
enabled: true
suppressionDuration: 5h
suppressionEnabled: false
techniques: []
alertRuleTemplateName:
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: 7d
matchingMethod: Selected
groupByCustomDetails:
- triage_id
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: Digital Shadows - {{EventMessage}}
alertDescriptionFormat: |-
{{description}}
{{impact}}
{{mitigation}}
alertSeverityColumnName: EventSeverity
customDetails:
triage_id: EventOriginalUid
severity: EventOriginalSeverity
description: description
status: status
impact: impact
mitigation: mitigation
version: 1.1.0
kind: Scheduled
Stages and Predicates
Let Binding: DSSearchLight_view
let DSSearchLight_view = view () { DigitalShadows_V2_CL | project-rename app_s = App, title_s = Title, raised_t = TimeRaised, updated_t = TimeUpdated, classification_s = Classification, risk_level_s = RiskLevel, risk_assessment_risk_level_s = RiskAssessmentRiskLevel, gm_link_s = GreyMatterLink, assets_s = Assets, comments_s = Comments, description_s = Description, impact_description_s = ImpactDescription, mitigation_s = Mitigation, risk_factors_s = RiskFactors, portal_id_s = PortalId, id_d = IncidentId, id_g = AlertId, status_s = Status, triage_id_g = TriageId, triage_raised_time_t = TriageRaisedTime, triage_updated_time_t = TriageUpdatedTime | where app_s == "exclude" | extend EventVendor="Digital Shadows", EventProduct="SearchLight",Type="DigitalShadows_V2_CL",EventStartTime=raised_t,EventMessage=title_s,EventOriginalUid=triage_id_g,EventOriginalType=classification_s | extend EventOriginalSeverity=iif(isempty(risk_level_s), risk_assessment_risk_level_s, risk_level_s) | extend EventSeverity = case(EventOriginalSeverity == 'none', 'Informational', EventOriginalSeverity == 'very-low', 'Low',EventOriginalSeverity == 'low', 'Low',EventOriginalSeverity == 'medium', 'Medium',EventOriginalSeverity == 'high', 'High',EventOriginalSeverity == 'very-high', 'High','Informational') | extend EventReportUrl = column_ifexists('gm_link_s', '') | extend AdditionalFields = pack("assets", assets_s, "comments", comments_s, "description", description_s, "incident_id", id_d, "alert_id", id_g, "short_code", portal_id_s, "impact", impact_description_s, "mitigation", mitigation_s, "risk_factors", risk_factors_s, "triage_status", status_s, "triage_id", triage_id_g, "triage_raised", triage_raised_time_t,"triage_updated", triage_updated_time_t, "updated", updated_t) | project TimeGenerated, EventVendor,EventProduct, Type, EventStartTime,EventMessage, EventOriginalUid, EventOriginalType,EventOriginalSeverity, EventSeverity, EventReportUrl,AdditionalFields};
Stage 1: source
DigitalShadows_V2_CL
Stage 2: project-rename
project-rename
Stage 3: where
where app_s == "exclude"
Stage 4: extend (5 consecutive steps)
extend AdditionalFields, EventMessage, EventOriginalSeverity, EventOriginalType, EventOriginalUid, EventProduct, EventReportUrl, EventSeverity, EventStartTime, EventVendor, Type
Stage 5: project
project AdditionalFields, EventMessage, EventOriginalSeverity, EventOriginalType, EventOriginalUid, EventProduct, EventReportUrl, EventSeverity, EventStartTime, EventVendor, TimeGenerated, Type
Stage 6: summarize
summarize by EventOriginalUid
Stage 7: extend (5 consecutive steps)
extend comments, description, impact, mitigation, status
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
app_s | eq |
| field:"App" kind:eq value:"exclude" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventOriginalUid | summarize |
description | extend |
impact | extend |
mitigation | extend |
status | extend |
comments | extend |
Google Threat Intelligence - Threat Hunting IP
#Google Threat Intelligence IP correlation.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
Rule Body
id: "7edb2abb-7ef7-4685-92eb-a628703ccf9f"
name: Google Threat Intelligence - Threat Hunting IP
description: |
'Google Threat Intelligence IP correlation.'
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'Google Threat Intelligence Match'
alertDescriptionFormat: 'Correlation found from the {{Type}} table.'
query: |
let ioc_lookBack = 1d;
_Im_NetworkSession
| where isnotempty(DstIpAddr)
| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'ipv4-addr:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
) on $left.DstIpAddr == $right.ObservableValue
| project NetworkIP=ObservableValue, Description=Data.description, Type, TimeGenerated
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NetworkIP
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let ioc_lookBack = 1d;
Stage 1: source
_Im_NetworkSession
Stage 2: where
| where isnotempty(DstIpAddr)
Stage 3: join
| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'ipv4-addr:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
) on $left.DstIpAddr == $right.ObservableValue
Stage 4: project
project Description, NetworkIP, TimeGenerated, Type
Stage 5: summarize aggregation inside the join branch
summarize by Id
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstIpAddr | is_not_null | field:"DstIpAddr" kind:is_not_null | |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ObservableKey | eq |
| field:"ObservableKey" kind:eq value:"ipv4-addr:value" |
ObservableValue | is_not_null | field:"ObservableValue" kind:is_not_null | |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"Google Threat Intelligence" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Id | summarize |
Google Threat Intelligence - Threat Hunting Url
#'Google Threat Intelligence Url correlation.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: "89290690-54c4-4196-91c5-d32b1df5d873"
name: Google Threat Intelligence - Threat Hunting Url
description: |
'Google Threat Intelligence Url correlation.'
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'Google Threat Intelligence Match'
alertDescriptionFormat: 'Correlation found from the {{Type}} table.'
query: |
let ioc_lookBack = 1d;
_Im_WebSession
| where isnotempty(Url)
| extend lowerUrl=tolower(Url)
| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'url:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
| extend lowerUrl=tolower(ObservableValue)
) on lowerUrl
| project Url=ObservableValue, Description=Data.description, Type, TimeGenerated
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let ioc_lookBack = 1d;
Stage 1: source
_Im_WebSession
Stage 2: where
| where isnotempty(Url)
Stage 3: extend
| extend lowerUrl=tolower(Url)
Stage 4: join
| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'url:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
| extend lowerUrl=tolower(ObservableValue)
) on lowerUrl
Stage 5: project
project Description, TimeGenerated, Type, Url
Stage 6: summarize aggregation inside the join branch
summarize by Id
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ObservableKey | eq |
| field:"ObservableKey" kind:eq value:"url:value" |
ObservableValue | is_not_null | field:"ObservableValue" kind:is_not_null | |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"Google Threat Intelligence" |
Url | is_not_null | field:"Url" kind:is_not_null | |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Id | summarize |
GSA - TI Domain Entity
#This query identifies Domain indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in GSA NetworkAccessTraffic.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 4ef07345-5d89-4f5b-9c64-a180d81a6176
name: GSA - TI Domain Entity
description: |
This query identifies Domain indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in GSA NetworkAccessTraffic.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- NetworkAccessTrafficLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let NetworkAccessTraff_ = NetworkAccessTraffic
| where TimeGenerated >= ago(dt_lookBack)
| where TrafficType == "internet"
| where isnotempty(DestinationFqdn)
| extend Domain = extract(@'([^.]+\.[^.]+)$', 1, DestinationFqdn)
| project-rename GSANetworkAccessTraffic_TimeGenerated = TimeGenerated;
let NetworkAccessTraffiDomains =
NetworkAccessTraff_
| distinct Domain;
ThreatIntelIndicators
| extend IndicatorType = replace(@'\[|\]|"""', "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "domain-name"
| extend DomainName = tolower(ObservableValue)
| where TimeGenerated >= ago(ioc_lookBack)
| extend IndicatorId = tostring(split(Id, "--")[2])
| where DomainName in (NetworkAccessTraffiDomains)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend Description = tostring(parse_json(Data).description)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
| join kind=innerunique (NetworkAccessTraff_) on $left.DomainName == $right.Domain
| extend GSAThreatType = ThreatType
| summarize GSANetworkAccessTraffic_TimeGenerated = arg_max(GSANetworkAccessTraffic_TimeGenerated, *) by Id, DestinationFqdn
| project-reorder *,
Tags,
TrafficLightProtocolLevel,
Type,
DomainName,
UserPrincipalName,
UserId,
DeviceId,
InitiatingProcessName,
GSAThreatType,
DestinationUrl,
DestinationPort,
SourceIp,
SourcePort,
HttpUserAgent
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserPrincipalName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: DestinationFqdn
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: InitiatingProcessName
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Let Binding: NetworkAccessTraff_
let NetworkAccessTraff_ = NetworkAccessTraffic
| where TimeGenerated >= ago(dt_lookBack)
| where TrafficType == "internet"
| where isnotempty(DestinationFqdn)
| extend Domain = extract(@'([^.]+\.[^.]+)$', 1, DestinationFqdn)
| project-rename GSANetworkAccessTraffic_TimeGenerated = TimeGenerated;
Let Binding: NetworkAccessTraffiDomains
let NetworkAccessTraffiDomains = NetworkAccessTraff_
| distinct Domain;
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@'\[|\]|"""', "", tostring(split(ObservableKey, ":", 0)))
Stage 3: where
| where IndicatorType == "domain-name"
Stage 4: extend
| extend DomainName = tolower(ObservableValue)
Stage 5: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 6: extend
| extend IndicatorId = tostring(split(Id, "--")[2])
Stage 7: where
| where DomainName in (NetworkAccessTraffiDomains)
Stage 8: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 9: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 10: extend
| extend Description = tostring(parse_json(Data).description)
Stage 11: extend
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
Stage 12: where
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
Stage 13: project-reorder
| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
Stage 14: join
| join kind=innerunique (NetworkAccessTraff_) on $left.DomainName == $right.Domain
Stage 15: extend
extend GSAThreatType
Stage 16: summarize
summarize GSANetworkAccessTraffic_TimeGenerated by Id, DestinationFqdn
Stage 17: project-reorder
project-reorder
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestinationFqdn | is_not_null | field:"DestinationFqdn" kind:is_not_null | |
DomainName | in |
| field:"DomainName" kind:in value:"NetworkAccessTraffiDomains" |
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"domain-name" |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
TrafficType | eq |
| field:"TrafficType" kind:eq value:"internet" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Description | contains | State: falsepos; | excludes:Description field:"Description" value:"State: falsepos;" |
Description | contains | State: inactive; | excludes:Description field:"Description" value:"State: inactive;" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DestinationFqdn | summarize |
GSANetworkAccessTraffic_TimeGenerated | summarize |
Id | summarize |
IaaS policy not attached to any identity
#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.
The rule detects AWS policies that are not attached to any identities, meaning they can be deleted.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Rule Body
id: 57bae0c4-50b7-4552-9de9-19dfecddbace
name: IaaS policy not attached to any identity
kind: Scheduled
description: The rule detects AWS policies that are not attached to any identities, meaning they can be deleted.
severity: Informational
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- Persistence
relevantTechniques:
- T1098
query: |-
Authomize_v2_CL
| where ingestion_time() >= ago(30m)
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
| where Policy has "IaaS policy not attached to any identity"
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
requiredDataConnectors:
- connectorId: Authomize
dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertnameFormat: Alert from Authomize - IaaS policy not attached to any identity
alertDescriptionFormat: IaaS policy not attached to any identity. The rule detects 'IaaS policies' attached to a role that has not used them during the past X days. It is recommended to remove unused policies from identities to reduce risk.
alertSeverity: Severity
alertTactics: Tactics
alertDynamicProperties:
- alertProperty: AlertLink
value: URL
customDetails:
AuthomizeEventID: EventID
EventName: Policy
EventDescription: Description
EventRecommendation: Recommendation
ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.3
Stages and Predicates
Stage 1: source
Authomize_v2_CL
Stage 2: where
| where ingestion_time() >= ago(30m)
Stage 3: extend
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
Stage 4: where
| where Policy has "IaaS policy not attached to any identity"
Stage 5: project
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Policy | match |
| field:"policy_name_s" kind:match value:"IaaS policy not attached to any identity" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Category | project |
Description | project |
EventID | project |
Policy | project |
Recommendation | project |
Severity | project |
Tactics | project |
URL | project |
License Support Expiring
#Detects when the Veeam support contract expires shortly.
Rule Body
id: 3caa6e6d-a3e2-4776-be97-15d20e2675ed
name: License Support Expiring
description: Detects when the Veeam support contract expires shortly.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "let license_editions_lookup = union isfuzzy=true (datatable(Edition:string,\
\ EditionDescription:string)[]), (_GetWatchlist(\"license_editions_lookup\")); \n\
let license_types_lookup = union isfuzzy=true (datatable(Type:string, TypeDescription:string)[]),\
\ (_GetWatchlist(\"license_types_lookup\")); \nVeeam_GetSecurityEvents\n| where\
\ instanceId == 24040\n| extend Edition = extract(\"Edition=\\\"([^\\\"]*)\\\"\"\
, 1, SyslogMessage)\n| lookup kind=leftouter (license_editions_lookup) \n on\
\ $left.Edition == $right.Edition\n| extend Type = extract(\"Type=\\\"([^\\\"]*)\\\
\"\", 1, SyslogMessage)\n| lookup kind=leftouter (license_types_lookup) \n on\
\ $left.Type == $right.Type\n| extend TenantID = extract(\"TenantID=\\\"([^\\\"\
]*)\\\"\", 1, SyslogMessage)\n| extend DaysLeft = extract(\"DaysLeft=\\\"([^\\\"\
]*)\\\"\", 1, SyslogMessage)\n| extend SupportLeft = extract(\"SupportLeft=\\\"\
([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n Date = format_datetime(TimeGenerated,\
\ 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n EventId = instanceId,\n\
\ [\"License Edition\"] = EditionDescription,\n [\"License Type\"] = TypeDescription,\n\
\ [\"Days Left\"] = DaysLeft,\n [\"Days of Support Left\"] = SupportLeft,\n\
\ MessageDetails = Description,\n Severity = SeverityDescription\n"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Parameters
let license_editions_lookup = union isfuzzy=true (datatable(Edition:string, EditionDescription:string)[]), (_GetWatchlist("license_editions_lookup"));
let license_types_lookup = union isfuzzy=true (datatable(Type:string, TypeDescription:string)[]), (_GetWatchlist("license_types_lookup"));
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 24040
Stage 3: extend
| extend Edition = extract("Edition=\"([^\"]*)\"", 1, SyslogMessage)
Stage 4: kusto:lookup
lookup kind=leftouter (license_editions_lookup) on $left.Edition, $right.Edition
Stage 5: extend
extend Type
Stage 6: kusto:lookup
lookup kind=leftouter (license_types_lookup) on $left.Type, $right.Type
Stage 7: extend (3 consecutive steps)
extend DaysLeft, SupportLeft, TenantID
Stage 8: project
project DataSource, Date, Days Left, Days of Support Left, EventId, License Edition, License Type, MessageDetails, Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"24040" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
Days Left | project |
Days of Support Left | project |
EventId | project |
License Edition | project |
License Type | project |
MessageDetails | project |
Severity | project |
New service account gained access to IaaS resource
#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 policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 6c17f270-cd56-48cc-9196-1728ffea6538
name: New service account gained access to IaaS resource
kind: Scheduled
description: This policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.
severity: Informational
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |-
Authomize_v2_CL
| where ingestion_time() >= ago(30m)
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
| where Policy has "New service account gained access to IaaS resource"
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
requiredDataConnectors:
- connectorId: Authomize
dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertnameFormat: Alert from Authomize - New service account gained access to IaaS resource
alertDescriptionFormat: New service account gained access to IaaS resource. This policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.
alertSeverity: Severity
alertTactics: Tactics
alertDynamicProperties:
- alertProperty: AlertLink
value: URL
customDetails:
AuthomizeEventID: EventID
EventName: Policy
EventDescription: Description
EventRecommendation: Recommendation
ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.3
Stages and Predicates
Stage 1: source
Authomize_v2_CL
Stage 2: where
| where ingestion_time() >= ago(30m)
Stage 3: extend
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
Stage 4: where
| where Policy has "New service account gained access to IaaS resource"
Stage 5: project
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Policy | match |
| field:"policy_name_s" kind:match value:"New service account gained access to IaaS resource" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Category | project |
Description | project |
EventID | project |
Policy | project |
Recommendation | project |
Severity | project |
Tactics | project |
URL | project |
New Sonrai Ticket
#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.
'Checks for new Sonrai tickets. It uses the action type to check if a ticket has been created'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Defense Impairment | |
| Credential Access | |
| Discovery | |
| Lateral Movement | |
| Collection | |
| Command & Control | |
| Exfiltration | |
| Impact |
Rule Body
id: bcc3362d-b6f9-4de0-b41c-707fafd5a416
name: New Sonrai Ticket
description: |
'Checks for new Sonrai tickets.
It uses the action type to check if a ticket has been created'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SonraiDataConnector
dataTypes:
- Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- CommandAndControl
- CredentialAccess
- DefenseEvasion
- Discovery
- Execution
- Exfiltration
- Impact
- InitialAccess
- LateralMovement
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1566
- T1059
- T1547
- T1548
- T1562
- T1003
- T1087
- T1021
- T1119
- T1071
- T1041
- T1499
query: |
Sonrai_Tickets_CL
| where action_d == 1
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: digest_criticalResourceName_s
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
ticketName: digest_title_s
ticketStatus: digest_status_s
resourceLabel: digest_resourceLabel_s
resourceType: digest_resourceType_s
criticalResource: digest_criticalResourceName_s
ticketOrg: digest_org_s
alertDetailsOverride:
alertSeverityColumnName: digest_severityCategory_s
alertDisplayNameFormat: New - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
Sonrai_Tickets_CL
Stage 2: where
| where action_d == 1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action_d | eq |
| field:"action_d" kind:eq value:"1" |
NGINX - Private IP address in URL
#'Detects requests to unusual URL'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 1aa6bfed-f11b-402f-9007-0dccc1152ede
name: NGINX - Private IP address in URL
description: |
'Detects requests to unusual URL'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
dataTypes:
- NGINX_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
NGINXHTTPServer
| where UrlOriginal matches regex @'(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.1[6-9]\.\d{1,3}\.\d{1,3})|(172\.2[0-9]\.\d{1,3}\.\d{1,3})|(172\.3[0-1]\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})'
| extend UrlCustomEntity = UrlOriginal
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
NGINXHTTPServer
Stage 2: where
| where UrlOriginal matches regex @'(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.1[6-9]\.\d{1,3}\.\d{1,3})|(172\.2[0-9]\.\d{1,3}\.\d{1,3})|(172\.3[0-1]\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})'
Stage 3: extend
| extend UrlCustomEntity = UrlOriginal
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
UrlOriginal | regex_match |
| field:"UrlOriginal" kind:regex_match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UrlCustomEntity | extend |
OracleDBAudit - User connected to database from new IP
#'Detects when a user connects to database from new IP address.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 39a0995e-f4a9-4869-a0ae-36d6d9049bfd
name: OracleDBAudit - User connected to database from new IP
description: |
'Detects when a user connects to database from new IP address.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
let lbtime_d = 14d;
let lbtime_24h = 24h;
OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_d) .. ago(lbtime_24h))
| where isnotempty(SrcIpAddr)
| where isnotempty(DstUserName)
| summarize knownIPs = make_set(SrcIpAddr) by DstUserName
| join (OracleDatabaseAuditEvent
| where isnotempty(SrcIpAddr)
| where isnotempty(DstUserName)
| where DbAction =~ 'connect'
) on DstUserName
| where knownIPs !contains SrcIpAddr
| project DstUserName, SrcIpAddr
| extend AccountCustomEntity = DstUserName
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let lbtime_d = 14d;
let lbtime_24h = 24h;
Stage 1: source
OracleDatabaseAuditEvent
Stage 2: where
| where TimeGenerated between (ago(lbtime_d) .. ago(lbtime_24h))
Stage 3: where
| where isnotempty(SrcIpAddr)
Stage 4: where
| where isnotempty(DstUserName)
Stage 5: summarize
| summarize knownIPs = make_set(SrcIpAddr) by DstUserName
Stage 6: join
| join (OracleDatabaseAuditEvent
| where isnotempty(SrcIpAddr)
| where isnotempty(DstUserName)
| where DbAction =~ 'connect'
) on DstUserName
Stage 7: where
where knownIPs !contains SrcIpAddr
Stage 8: project
project DstUserName, SrcIpAddr
Stage 9: extend
extend AccountCustomEntity
Stage 10: extend
extend IPCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DbAction | eq |
| field:"DbAction" kind:eq value:"connect" |
DstUserName | is_not_null | field:"DstUserName" kind:is_not_null | |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | project |
SrcIpAddr | project |
AccountCustomEntity | extend |
IPCustomEntity | extend |
Pathlock TDnR - Kerberos Keytab Changes
#Detects changes to Kerberos keytab configuration in SAP, forwarded by Pathlock Threat Detection and Response. Modifications to Kerberos settings may indicate credential theft, SSO bypass attempts, or persistent access mechanisms leveraging Kerberos delegation.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Credential Access |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c12
name: Pathlock TDnR - Kerberos Keytab Changes
kind: Scheduled
description: >-
Detects changes to Kerberos keytab configuration in SAP, forwarded by Pathlock Threat Detection
and Response. Modifications to Kerberos settings may indicate credential theft, SSO bypass
attempts, or persistent access mechanisms leveraging Kerberos delegation.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
relevantTechniques:
- T1558
- T1098
query: |
Pathlock_TDnR_CL
| where DataSource == "CHANGEDOC_KERBEROS"
| 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_KERBEROS"
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_KERBEROS" |
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 |
Ping Federate - Unexpected country for user
#'Detects requests from different countries for user in shotr term.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 64e65105-c4fc-4c28-a4e9-bb1a3ce7652d
name: Ping Federate - Unexpected country for user
description: |
'Detects requests from different countries for user in shotr term.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
let known_countries =
PingFederateEvent
| where TimeGenerated between (ago(1d) .. (1h))
| where isnotempty(DstGeoCountry)
| summarize makeset(DstGeoCountry);
PingFederateEvent
| where isnotempty(DstGeoCountry)
| where DstGeoCountry !in (known_countries)
| extend AccountCustomEntity = DstUserName
| extend IpCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IpCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Let Binding: known_countries
let known_countries = PingFederateEvent
| where TimeGenerated between (ago(1d) .. (1h))
| where isnotempty(DstGeoCountry)
| summarize makeset(DstGeoCountry);
Stage 1: source
PingFederateEvent
Stage 2: where
| where isnotempty(DstGeoCountry)
Stage 3: where
| where DstGeoCountry !in (known_countries)
Stage 4: extend
| extend AccountCustomEntity = DstUserName
Stage 5: extend
| extend IpCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstGeoCountry | is_not_null | field:"DstGeoCountry" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstGeoCountry | eq | known_countries | excludes:DstGeoCountry field:"DstGeoCountry" value:"known_countries" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IpCustomEntity | extend |
Potential DHCP Starvation Attack
#'This creates an incident in the event that an excessive amount of DHCPREQUEST have been recieved by a DHCP Server and could potentially be an indication of a DHCP Starvation Attack.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 57e56fc9-417a-4f41-a579-5475aea7b8ce
name: Potential DHCP Starvation Attack
description: |
'This creates an incident in the event that an excessive amount of DHCPREQUEST have been recieved by a DHCP Server and could potentially be an indication of a DHCP Starvation Attack.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1200
query: |
let threshold = 1000;
Infoblox
| where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
| summarize count() by SrcIpAddr, bin(TimeGenerated,5m)
| where count_ > threshold
| join kind=inner (Infoblox
| where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
) on SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
version: 1.0.5
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 1000;
Stage 1: source
Infoblox
Stage 2: where
| where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
Stage 3: summarize
| summarize count() by SrcIpAddr, bin(TimeGenerated,5m)
Stage 4: where
| where count_ > threshold
Stage 5: join
| join kind=inner (Infoblox
| where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
) on SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Log_Type | eq |
| field:"Log_Type" kind:eq value:"DHCPREQUEST" |
ProcessName | eq |
| field:"process_name" kind:eq value:"dhcpd" |
count_ | gt |
| field:"count_" kind:gt value:"1000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | summarize |
PROD (TM015.1) - ACL - Modified at KRBTGT or AdminSDHolder Object Level
#ACL changes to the KRBTGT or AdminSDHolder special objects.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 5136: A directory service object was modified. |
Rule Body
id: a9f94bf1-856e-40ff-a176-24f967da1e27
name: 'PROD (TM015.1) - ACL - Modified at KRBTGT or AdminSDHolder Object Level'
description: |
ACL changes to the KRBTGT or AdminSDHolder special objects.
severity: High
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- Persistence
relevantTechniques:
- T1078.002
query: |
SecurityEvent
| where EventID == 5136
and (EventData matches regex @"(?i)CN=AdminSDHolder" or EventData matches regex @"(?i)CN=krbtgt")
and Activity has "modified"
and (EventData has '"ObjectClass">container' or EventData matches regex @"(?i)user")
and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>'
and EventData has '"OperationType">%%14674'
| extend ObjectName = extract("<Data Name=\"ObjectDN\">(((?i)CN=[^,]+,?)+),[dD][cC]=", 1, EventData)
| extend ObjectTier = "Tier 0"
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
| project TimeGenerated
, ObjectName
, ObjectTier
, ObjectClass
, OperationType
, ActivityType
, AttributeLDAP
, Account
, Domain
, Computer
, Channel
, EventID
, EventData
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ObjectName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Computer
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Account
customDetails:
ObjectName: ObjectName
ObjectTier: ObjectTier
ObjectClass: ObjectClass
OperationType: OperationType
ActivityType: ActivityType
AttributeLDAP: AttributeLDAP
Account: Account
Domain: Domain
Computer: Computer
Channel: Channel
EventID: EventID
EventData: EventData
eventGroupingSettings:
aggregationKind: AlertPerResult
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: '(TM015.1) The {{ObjectTier}} {{ObjectName}} ACL was modified'
alertDescriptionFormat: 'The following user {{Account}} modified the {{ObjectName}} ACLs.'
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where EventID == 5136
and (EventData matches regex @"(?i)CN=AdminSDHolder" or EventData matches regex @"(?i)CN=krbtgt")
and Activity has "modified"
and (EventData has '"ObjectClass">container' or EventData matches regex @"(?i)user")
and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>'
and EventData has '"OperationType">%%14674'
Stage 3: extend (7 consecutive steps)
| extend ObjectName = extract("<Data Name=\"ObjectDN\">(((?i)CN=[^,]+,?)+),[dD][cC]=", 1, EventData)
| extend ObjectTier = "Tier 0"
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
Stage 4: project
| project TimeGenerated
, ObjectName
, ObjectTier
, ObjectClass
, OperationType
, ActivityType
, AttributeLDAP
, Account
, Domain
, Computer
, Channel
, EventID
, EventData
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Activity | match |
| field:"Activity" kind:match value:"modified" |
EventData | match |
| field:"EventData" kind:match |
EventData | regex_match |
| field:"EventData" kind:regex_match |
EventID | eq |
| field:"EventID" kind:eq value:"5136" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
ActivityType | project |
AttributeLDAP | project |
Channel | project |
Computer | project |
Domain | project |
EventData | project |
EventID | project |
ObjectClass | project |
ObjectName | project |
ObjectTier | project |
OperationType | project |
TimeGenerated | project |
Remote Desktop Protocol - SharpRDP
#This detection monitors for the behavior that SharpRDP exhibits on the target system. The most relevant is leveraging taskmgr.exe to gain elevated execution, which means that taskmgr.exe is creating unexpected child processes.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Telemetry Coverage
Rule Body
id: cc46e76c-0d04-40b0-9c8b-929aa40513e7
name: Remote Desktop Protocol - SharpRDP
description: |
This detection monitors for the behavior that SharpRDP exhibits on the target system. The most relevant is leveraging taskmgr.exe to gain elevated execution, which means that taskmgr.exe is creating unexpected child processes.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
- DeviceLogonEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T1021.001
query: |
let executions = DeviceProcessEvents
| where InitiatingProcessFileName contains "taskmgr" and AccountName !contains "_ladm" // Include your LAPS /RID500 admin naming convention here.
// Add the below filter to look for stock SharpRDP behavior. However, with minorchanges to the stock version of SharpRDP the filter below can be bypassed.
//| where not(InitiatingProcessCommandLine has_any ("/1","/2","/3","/4"))
| where not(FolderPath =~ @"c:\Windows\system32\WerFault.exe" and ProcessCommandLine contains "-u -p")
| where not(FolderPath =~ @"c:\windows\system32\mmc.exe" and ProcessCommandLine contains @"C:\WINDOWS\System32\services.msc")
| where not(FolderPath =~ @"c:\windows\system32\resmon.exe");
executions
| join kind=leftsemi (DeviceLogonEvents
| where LogonType in ('Unlock', 'RemoteInteractive') and not (LogonType == 'Unlock' and RemoteIP == '127.0.0.1') and RemoteIP != "" and ActionType == "LogonSuccess") on DeviceId, LogonId
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: AccountSid
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: AccountDomain
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled
Stages and Predicates
let executions is inlined into the numbered stages below.
Stage 1: source
let executions
Stage 2: source
DeviceProcessEvents
Stage 3: where
where not (AccountName contains "_ladm") and InitiatingProcessFileName contains "taskmgr"
Stage 4: where
where not ((FolderPath =~ @"c:\Windows\system32\WerFault.exe" and ProcessCommandLine contains "-u -p"))
Stage 5: where
where not ((FolderPath =~ @"c:\windows\system32\mmc.exe" and ProcessCommandLine contains @"C:\WINDOWS\System32\services.msc"))
Stage 6: where
where not (FolderPath =~ @"c:\windows\system32\resmon.exe")
Stage 7: join
join kind=leftsemi (DeviceLogonEvents) on DeviceId, LogonId
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"LogonSuccess" |
InitiatingProcessFileName | contains |
| field:"InitiatingProcessFileName" kind:contains value:"taskmgr" |
LogonType | in |
| field:"LogonType" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
AccountName | contains | _ladm | excludes:AccountName field:"AccountName" value:"_ladm" |
FolderPath | eq | c:\Windows\system32\WerFault.exe | excludes:FolderPath field:"FolderPath" value:"c:\Windows\system32\WerFault.exe" |
ProcessCommandLine | contains | -u -p | excludes:ProcessCommandLine field:"ProcessCommandLine" value:"-u -p" |
FolderPath | eq | c:\windows\system32\mmc.exe | excludes:FolderPath field:"FolderPath" value:"c:\windows\system32\mmc.exe" |
ProcessCommandLine | contains | C:\WINDOWS\System32\services.msc | excludes:ProcessCommandLine field:"ProcessCommandLine" value:"C:\WINDOWS\System32\services.msc" |
FolderPath | eq | c:\windows\system32\resmon.exe | excludes:FolderPath field:"FolderPath" value:"c:\windows\system32\resmon.exe" |
LogonType | eq | Unlock | excludes:LogonType field:"LogonType" value:"Unlock" |
RemoteIP | eq | 127.0.0.1 | excludes:RemoteIP field:"RemoteIP" value:"127.0.0.1" |
Sonrai Ticket Reopened
#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.
'Checks if Sonrai tickets have been reopened. It uses the action type to check if a ticket has been reopened'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Defense Impairment | |
| Credential Access | |
| Discovery | |
| Lateral Movement | |
| Collection | |
| Command & Control | |
| Exfiltration | |
| Impact |
Rule Body
id: b60129ab-ce22-4b76-858d-3204932a13cc
name: Sonrai Ticket Reopened
description: |
'Checks if Sonrai tickets have been reopened.
It uses the action type to check if a ticket has been reopened'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SonraiDataConnector
dataTypes:
- Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- CommandAndControl
- CredentialAccess
- DefenseEvasion
- Discovery
- Execution
- Exfiltration
- Impact
- InitialAccess
- LateralMovement
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1566
- T1059
- T1547
- T1548
- T1562
- T1003
- T1087
- T1021
- T1119
- T1071
- T1041
- T1499
query: |
Sonrai_Tickets_CL
| where action_d == 3
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: digest_criticalResourceName_s
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
ticketName: digest_title_s
ticketStatus: digest_status_s
ticketSeverity: digest_severityCategory_s
resourceLabel: digest_resourceLabel_s
resourceType: digest_resourceType_s
criticalResource: digest_criticalResourceName_s
ticketOrg: digest_org_s
reopenedDate: digest_lastReopenDate_d
alertDetailsOverride:
alertSeverityColumnName: digest_severityCategory_s
alertDisplayNameFormat: Reopened - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
Sonrai_Tickets_CL
Stage 2: where
| where action_d == 3
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action_d | eq |
| field:"action_d" kind:eq value:"3" |
Tailscale: MagicDNS disabled
#Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment | |
| Credential Access |
Rule Body
id: f8a9b0c1-4567-8901-23ab-cdef12345020
name: "Tailscale: MagicDNS disabled"
description: |
Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: TailscaleCCF
dataTypes:
- Tailscale_Audit_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1556
query: |
Tailscale_Audit_CL
| where Action == "UPDATE"
| where tostring(Target.type) == "TAILNET"
| where tostring(Target.property) == "MAGICDNS_PREFERENCES"
| extend ActorLogin = tostring(Actor.loginName)
| extend OldMagicDns = tobool(Old.magicDNS)
| extend NewMagicDns = tobool(New.magicDNS)
| where OldMagicDns == true and NewMagicDns == false
| project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorLogin
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P1D
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0
Stages and Predicates
Stage 1: source
Tailscale_Audit_CL
Stage 2: where
| where Action == "UPDATE"
Stage 3: where
| where tostring(Target.type) == "TAILNET"
Stage 4: where
| where tostring(Target.property) == "MAGICDNS_PREFERENCES"
Stage 5: extend (3 consecutive steps)
| extend ActorLogin = tostring(Actor.loginName)
| extend OldMagicDns = tobool(Old.magicDNS)
| extend NewMagicDns = tobool(New.magicDNS)
Stage 6: where
| where OldMagicDns == true and NewMagicDns == false
Stage 7: project
| project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | eq |
| field:"Action" kind:eq value:"UPDATE" |
NewMagicDns | eq |
| field:"NewMagicDns" kind:eq value:"false" |
OldMagicDns | eq |
| field:"OldMagicDns" kind:eq value:"true" |
Target.property | eq |
| field:"Target.property" kind:eq value:"MAGICDNS_PREFERENCES" |
Target.type | eq |
| field:"Target.type" kind:eq value:"TAILNET" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActorLogin | project |
NewMagicDns | project |
OldMagicDns | project |
Origin | project |
TimeGenerated | project |
TI Map IP Entity to VMConnection
#This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in VMConnection.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: aed70d71-adb2-4f73-becd-02150b13950b
name: TI Map IP Entity to VMConnection
description: |
This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in VMConnection.
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h; // Look back 1 hour for VMConnection events
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
| extend NetworkSourceIP = toupper(ObservableValue)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
| 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 Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil));
// Perform a join between IP indicators and VMConnection events
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
// 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 (
VMConnection
| where TimeGenerated >= ago(dt_lookBack)
| extend VMConnection_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.RemoteIp
// Filter out VMConnection events that occurred after the expiration of the corresponding indicator
| where VMConnection_TimeGenerated < ValidUntil
// Group the results by IndicatorId and keep the VMConnection event with the latest timestamp
| summarize VMConnection_TimeGenerated = arg_max(VMConnection_TimeGenerated, *) by Id, RemoteIp
// Select the desired output fields
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project VMConnection_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence,
TI_ipEntity, Computer, Direction, ProcessName, SourceIp, DestinationIp, RemoteIp, Protocol, DestinationPort, NetworkSourceIP, Type, Url
| extend timestamp = VMConnection_TimeGenerated, HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: RemoteIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.4.6
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 10 define let IP_Indicators as the rule's main pipeline source. Stages 11 to 18 use it.
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 3: where
| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
Stage 4: extend
| extend NetworkSourceIP = toupper(ObservableValue)
Stage 5: extend
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
Stage 6: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 7: extend (3 consecutive steps)
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
TI_ipEntity =isnotempty(NetworkSourceIP)NetworkSourceIPNetworkSourceIPStage 8: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 9: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 10: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 11: project-reorder
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
Stage 12: join
| join kind=innerunique (
VMConnection
| where TimeGenerated >= ago(dt_lookBack)
| extend VMConnection_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.RemoteIp
Stage 13: where
where VMConnection_TimeGenerated < ValidUntil
Stage 14: summarize
summarize VMConnection_TimeGenerated by Id, RemoteIp
Stage 15: extend
extend Description
Stage 16: extend
extend ActivityGroupNames
Stage 17: project
project ActivityGroupNames, Computer, Confidence, Description, DestinationIp, DestinationPort, Direction, Id, NetworkSourceIP, ProcessName, Protocol, RemoteIp, SourceIp, TI_ipEntity, Type, Url, VMConnection_TimeGenerated, ValidUntil
Stage 18: extend
extend DnsDomain, HostName, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | in |
| field:"IndicatorType" kind:in |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ValidUntil | is_null | field:"ValidUntil" kind:is_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 |
Computer | project |
Confidence | project |
Description | project |
DestinationIp | project |
DestinationPort | project |
Direction | project |
Id | project |
NetworkSourceIP | project |
ProcessName | project |
Protocol | project |
RemoteIp | project |
SourceIp | project |
TI_ipEntity | project |
Type | project |
Url | project |
VMConnection_TimeGenerated | project |
ValidUntil | project |
DnsDomain | extend |
HostName | extend |
timestamp | extend |
Tomcat - Commands in URI
#'Detects commands in URI'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 91f59cea-486f-11ec-81d3-0242ac130003
name: Tomcat - Commands in URI
description: |
'Detects commands in URI'
severity: High
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
datatypes:
- Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
let commands = dynamic(['cat%20/etc/passwd', '/etc/passwd', 'ping -i', '/usr/bin/id(', '%2f%75%73%72%2f%62%69%6e%2f%69%64', 'phpinfo()', '%70%68%70%69%6e%66%6f%28%29', ';id', '%3b%69%64', '/bin/bash -c', '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27', '/bin/bash', '%2f%62%69%6e%2f%62%61%73%68', 'sleep(', '%73%6c%65%65%70%28', 'curl', '%63%75%72%6c', '&dir', '%26%64%69%72', '& dir', '%26%20%64%69%72', '<script>', '%3c%73%63%72%69%70%74%3e', 'eval(', '%65%76%61%6c%28', 'exec(', '%65%78%65%63%28', 'whoami', '%77%68%6f%61%6d%69', 'wget', 'python', 'gcc', 'uname', 'systeminfo', '%70%79%74%68%6f%6e', '%75%6e%61%6d%65', '%73%79%73%74%65%6d%69%6e%66%6f']);
TomcatEvent
| where UrlOriginal has_any (commands)
| extend UrlCustomEntity = UrlOriginal
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Let Binding: commands
let commands = dynamic(['cat%20/etc/passwd', '/etc/passwd', 'ping -i', '/usr/bin/id(', '%2f%75%73%72%2f%62%69%6e%2f%69%64', 'phpinfo()', '%70%68%70%69%6e%66%6f%28%29', ';id', '%3b%69%64', '/bin/bash -c', '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27', '/bin/bash', '%2f%62%69%6e%2f%62%61%73%68', 'sleep(', '%73%6c%65%65%70%28', 'curl', '%63%75%72%6c', '&dir', '%26%64%69%72', '& dir', '%26%20%64%69%72', '<script>', '%3c%73%63%72%69%70%74%3e', 'eval(', '%65%76%61%6c%28', 'exec(', '%65%78%65%63%28', 'whoami', '%77%68%6f%61%6d%69', 'wget', 'python', 'gcc', 'uname', 'systeminfo', '%70%79%74%68%6f%6e', '%75%6e%61%6d%65', '%73%79%73%74%65%6d%69%6e%66%6f']);
Stage 1: source
TomcatEvent
Stage 2: where
| where UrlOriginal has_any (commands)
Stage 3: extend
| extend UrlCustomEntity = UrlOriginal
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
UrlOriginal | match |
| field:"UrlOriginal" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UrlCustomEntity | extend |
Unusual Anomaly
#'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'
Rule Body
id: d0255b5f-2a3c-4112-8744-e6757af3283a
name: Unusual Anomaly
description: |
'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 4d
triggerOperator: gt
triggerThreshold: 0
tactics: []
techniques: []
query: |
// You can leave out Anomalies that are already monitored through other Analytics Rules
//let _MonitoredRules = dynamic(["TestAlertName"]);
let query_frequency = 1h;
let query_lookback = 3d;
Anomalies
| where TimeGenerated > ago(query_frequency)
//| where not(RuleName has_any (_MonitoredRules))
| join kind = leftanti (
Anomalies
| where TimeGenerated between (ago(query_frequency + query_lookback)..ago(query_frequency))
| distinct RuleName
) on RuleName
| extend Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: Unusual Anomaly - {{RuleName}}
alertTacticsColumnName: Tactics
alertDynamicProperties:
- alertProperty: Techniques
value: Techniques
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let query_frequency = 1h;
let query_lookback = 3d;
Stage 1: source
Anomalies
Stage 2: where
| where TimeGenerated > ago(query_frequency)
Stage 3: join (negated)
join kind=leftanti (Anomalies) on RuleName
Stage 4: extend
extend Name, UPNSuffix
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Name | extend |
UPNSuffix | extend |
Vectra RUX - Create Incident for Escalated Account Detection or Unresolved Priority Account
#This query creates a Microsoft Sentinel incident when a detection on an account entity has been escalated in Vectra (investigation_status = escalated). Escalation indicates that a human analyst or MDR service has reviewed the detection and determined it requires immediate customer attention or notification. One incident is created per detection - grouping is based on alert display name so that repeated rule evaluations against the same detection do not create duplicate incidents. This query creates a Microsoft Sentinel incident when Vectra AI identifies an account entity with one or more detections marked as unresolved and prioritized (unresolved_priority = true). Vectra's AI engine assigns priority based on attack rating, velocity, breadth, and identity privilege level. This rule surfaces account-based threats that require analyst attention. Incidents are grouped per account entity and remain open while the threat persists. Once all associated detections are resolved in Vectra, the incident can be closed in Microsoft Sentinel.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Credential Access | |
| Command & Control | |
| Exfiltration |
Rule Body
id: 231904f5-b670-4223-9bec-2e9aeca9cf5a
name: Vectra RUX - Create Incident for Escalated Account Detection or Unresolved Priority Account
version: 1.0.0
kind: Scheduled
description: |
This query creates a Microsoft Sentinel incident when a detection on an account entity has been escalated in Vectra
(investigation_status = escalated). Escalation indicates that a human analyst or MDR service
has reviewed the detection and determined it requires immediate customer attention or notification.
One incident is created per detection - grouping is based on alert display name so that
repeated rule evaluations against the same detection do not create duplicate incidents.
This query creates a Microsoft Sentinel incident when Vectra AI identifies an account entity with one or more detections
marked as unresolved and prioritized (unresolved_priority = true). Vectra's AI engine assigns
priority based on attack rating, velocity, breadth, and identity privilege level. This rule surfaces
account-based threats that require analyst attention.
Incidents are grouped per account entity and remain open while the threat persists. Once all associated
detections are resolved in Vectra, the incident can be closed in Microsoft Sentinel.
status: Available
requiredDataConnectors:
- connectorId: VectraRUXConnector
dataTypes:
- VectraDetectionsCombined
severity: High
queryFrequency: 10m
queryPeriod: 10m
suppressionDuration: PT1H
suppressionEnabled: false
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- Discovery
- LateralMovement
- CredentialAccess
- Exfiltration
- CommandAndControl
- Persistence
relevantTechniques:
- T1078
- T1110
- T1003
- T1041
- T1071
query: |
VectraDetectionsCombined
| where ["Entity Type"] == "account"
| summarize arg_max(TimeGenerated, *) by ["Detection ID"]
| where ["Investigation Status"] == "escalated" or ["Unresolved Priority"] == true
| extend
detection_name = ["D Type Vname"],
detection_category = ["Detection Category"],
entity_url = replace_string(replace_string(URL, "api/v3.4/", ""), "api/v3.5/", ""),
mitre_techniques = tostring(Mitre),
detection_tags = tostring(Tags),
assigned_to = ["Assigned To"],
entity_name = ["Entity Name"],
entity_uid = ["Entity UID"],
entity_id = ["Entity ID"],
detection_id = ["Detection ID"],
external_reference = ["External Reference ID"],
investigation_status = ["Investigation Status"],
entity_type = ["Entity Type"]
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: entity_name
- identifier: UPNSuffix
columnName: entity_uid
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P7D
matchingMethod: Selected
groupByEntities:
- Account
groupByAlertDetails:
- DisplayName
customDetails:
detection_name: detection_name
assigned_to: assigned_to
entity_uid: entity_uid
entity_url: entity_url
entity_id: entity_id
detection_id: detection_id
external_reference: external_reference
investigation_status: investigation_status
category: detection_category
tags: detection_tags
entity_type: entity_type
mitre_techniques: mitre_techniques
alertDetailsOverride:
alertDisplayNameFormat: "Vectra AI - {{detection_name}} on {{entity_name}}"
alertDescriptionFormat: >
Vectra AI has escalated detection {{detection_name}} ({{detection_category}}) on account or identified
account {{entity_name}} as a priority threat. See custom details for full context.
alertDynamicProperties:
- alertProperty: AlertLink
value: entity_url
- alertProperty: Techniques
value: mitre_techniques
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Stage 1: source
VectraDetectionsCombined
Stage 2: where
| where ["Entity Type"] == "account"
Stage 3: summarize
| summarize arg_max(TimeGenerated, *) by ["Detection ID"]
Stage 4: where
| where ["Investigation Status"] == "escalated" or ["Unresolved Priority"] == true
Stage 5: extend
| extend
detection_name = ["D Type Vname"],
detection_category = ["Detection Category"],
entity_url = replace_string(replace_string(URL, "api/v3.4/", ""), "api/v3.5/", ""),
mitre_techniques = tostring(Mitre),
detection_tags = tostring(Tags),
assigned_to = ["Assigned To"],
entity_name = ["Entity Name"],
entity_uid = ["Entity UID"],
entity_id = ["Entity ID"],
detection_id = ["Detection ID"],
external_reference = ["External Reference ID"],
investigation_status = ["Investigation Status"],
entity_type = ["Entity Type"]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Entity Type | eq |
| field:"Entity Type" kind:eq value:"account" |
Investigation Status | eq |
| field:"Investigation Status" kind:eq value:"escalated" |
Unresolved Priority | eq |
| field:"Unresolved Priority" kind:eq value:"true" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
assigned_to | extend |
detection_category | extend |
detection_id | extend |
detection_name | extend |
detection_tags | extend |
entity_id | extend |
entity_name | extend |
entity_type | extend |
entity_uid | extend |
entity_url | extend |
external_reference | extend |
investigation_status | extend |
mitre_techniques | extend |
Veeam ONE Unusual Job Duration (Veeam Backup for Microsoft 365)
#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 Veeam Backup for Microsoft 365 jobs with unusual execution duration.
Rule Body
id: cab7396a-9a18-45e7-b685-945e8bf825cb
name: Veeam ONE Unusual Job Duration (Veeam Backup for Microsoft 365)
description: Detects Veeam Backup for Microsoft 365 jobs with unusual execution duration.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
dataTypes:
- VeeamOneTriggeredAlarms_CL
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 381
version: 1.0.1
kind: Scheduled
customDetails:
PredefinedAlarmId: PredefinedAlarmId
TriggeredAlarmId: TriggeredAlarmId
Description: Description
Name: Name
VoneHostName: VoneHostName
TriggeredTime: TriggeredTime
Status: Status
Comment: Comment
ObjectId: ObjectId
ObjectName: ObjectName
ObjectType: ObjectType
Stages and Predicates
Stage 1: source
VeeamOneTriggeredAlarms_CL
Stage 2: where
| where PredefinedAlarmId == 381
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PredefinedAlarmId | eq |
| field:"PredefinedAlarmId" kind:eq value:"381" |
VMware SD-WAN Edge - IDS/IPS Alert triggered (Syslog)
#The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available. This analytics rule analyzes Syslog streams.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Rule Body
id: a8e2bfd2-5d9c-4acc-aa55-30029e50d574
name: VMware SD-WAN Edge - IDS/IPS Alert triggered (Syslog)
version: 1.0.0
kind: Scheduled
description: |-
The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available.
This analytics rule analyzes Syslog streams.
severity: High
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
requiredDataConnectors:
- connectorId: VMwareSDWAN
dataTypes:
- SDWAN
tactics:
- LateralMovement
relevantTechniques:
- T1210
query: |-
Syslog
| where SyslogMessage contains "VCF Alert"
| project-rename EdgeName=HostName
| project-away Computer, HostIP, SourceSystem, Type
| extend IdpsSignatureName = extract("SIGNATURE=(.+) CATEGORY=", 1, SyslogMessage)
| extend IdpsAlertCategory = extract("CATEGORY=(.+) SEVERITY=", 1, SyslogMessage)
| extend IdpsAlertSeverity = extract("SEVERITY=(.+) SRC_IP=", 1, SyslogMessage)
| extend IdpsSignatureId = extract("SIG_ID=([0-9]+) SIGNATURE=", 1, SyslogMessage)
| extend OverlaySegmentName = extract("SEGMENT_NAME=(.+) SIG_ID=", 1, SyslogMessage)
| extend IpProtocol = extract("PROTO=([A-Z]+) SRC=", 1, SyslogMessage)
| extend SrcIpAddress = extract("SRC=(.+) DST=", 1, SyslogMessage)
| extend SrcPort = extract("SPT=([0-9]+) DPT=", 1, SyslogMessage)
| extend DstIpAddress = extract("DST=(.+) SPT=", 1, SyslogMessage)
| extend DstPort = extract("DPT=(.+) DEST_DOMAIN=", 1, SyslogMessage)
| extend VictimIp = extract("TARGET_IP=(.+) TARGET_PORT=", 1, SyslogMessage)
| extend AttackerIp = extract("SRC_IP=(.+) SRC_PORT=", 1, SyslogMessage)
| extend DomainName = extract("DEST_DOMAIN=(.+) FW_POLICY_NAME=", 1, SyslogMessage)
| extend EdgeFwAction = extract("ATP_ACTION=(.+) SEGMENT=", 1, SyslogMessage)
| extend SyslogTag = extract("$(.+): ACTION=", 1, SyslogMessage)
| extend FwPolicyName = extract("FW_POLICY_NAME=(.+) SEGMENT_NAME=", 1, SyslogMessage)
| project
TimeGenerated,
IdpsSignatureName,
IdpsAlertSeverity,
IdpsAlertCategory,
IdpsSignatureId,
EdgeFwAction,
EdgeName,
SrcIpAddress,
IpProtocol,
SrcPort,
DstIpAddress,
DstPort,
DomainName,
AttackerIp,
VictimIp,
FwPolicyName,
SyslogTag
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
IDPS_Signature: IdpsSignatureName
IDPS_Event_Category: IdpsAlertCategory
Edge_Name: EdgeName
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DomainName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: VictimIp
suppressionDuration: 5h
Stages and Predicates
Stage 1: source
Syslog
Stage 2: where
| where SyslogMessage contains "VCF Alert"
Stage 3: project-rename
| project-rename EdgeName=HostName
Stage 4: project-away
| project-away Computer, HostIP, SourceSystem, Type
Stage 5: extend (16 consecutive steps)
| extend IdpsSignatureName = extract("SIGNATURE=(.+) CATEGORY=", 1, SyslogMessage)
| extend IdpsAlertCategory = extract("CATEGORY=(.+) SEVERITY=", 1, SyslogMessage)
| extend IdpsAlertSeverity = extract("SEVERITY=(.+) SRC_IP=", 1, SyslogMessage)
| extend IdpsSignatureId = extract("SIG_ID=([0-9]+) SIGNATURE=", 1, SyslogMessage)
| extend OverlaySegmentName = extract("SEGMENT_NAME=(.+) SIG_ID=", 1, SyslogMessage)
| extend IpProtocol = extract("PROTO=([A-Z]+) SRC=", 1, SyslogMessage)
| extend SrcIpAddress = extract("SRC=(.+) DST=", 1, SyslogMessage)
| extend SrcPort = extract("SPT=([0-9]+) DPT=", 1, SyslogMessage)
| extend DstIpAddress = extract("DST=(.+) SPT=", 1, SyslogMessage)
| extend DstPort = extract("DPT=(.+) DEST_DOMAIN=", 1, SyslogMessage)
| extend VictimIp = extract("TARGET_IP=(.+) TARGET_PORT=", 1, SyslogMessage)
| extend AttackerIp = extract("SRC_IP=(.+) SRC_PORT=", 1, SyslogMessage)
| extend DomainName = extract("DEST_DOMAIN=(.+) FW_POLICY_NAME=", 1, SyslogMessage)
| extend EdgeFwAction = extract("ATP_ACTION=(.+) SEGMENT=", 1, SyslogMessage)
| extend SyslogTag = extract("$(.+): ACTION=", 1, SyslogMessage)
| extend FwPolicyName = extract("FW_POLICY_NAME=(.+) SEGMENT_NAME=", 1, SyslogMessage)
Stage 6: project
| project
TimeGenerated,
IdpsSignatureName,
IdpsAlertSeverity,
IdpsAlertCategory,
IdpsSignatureId,
EdgeFwAction,
EdgeName,
SrcIpAddress,
IpProtocol,
SrcPort,
DstIpAddress,
DstPort,
DomainName,
AttackerIp,
VictimIp,
FwPolicyName,
SyslogTag
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
SyslogMessage | contains |
| field:"SyslogMessage" kind:contains value:"VCF Alert" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AttackerIp | project |
DomainName | project |
DstIpAddress | project |
DstPort | project |
EdgeFwAction | project |
EdgeName | project |
FwPolicyName | project |
IdpsAlertCategory | project |
IdpsAlertSeverity | project |
IdpsSignatureId | project |
IdpsSignatureName | project |
IpProtocol | project |
SrcIpAddress | project |
SrcPort | project |
SyslogTag | project |
TimeGenerated | project |
VictimIp | project |
Votiro - File Blocked in Email
#'The analytic rule is designed to identify when an email is blocked by Votiro Sanitization Engine policy. The rule generates an alert when an email is blocked after Sanitization process which is not password protected and has a from field meaning its a valid email. More information in terms of details about the blocked attachment, such as the file name and hash, and information about the email, such as the sender and recipient(s). The alert also includes information about the Votiro policy that blocked the attachment and provides a link to additional details about the incident.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Stealth | |
| Impact | |
| Command and Control |
Rule Body
id: 0b8b91de-c63e-4bc2-b5f4-b15d3b379ec9
name: Votiro - File Blocked in Email
description: |
'The analytic rule is designed to identify when an email is blocked by Votiro Sanitization Engine policy. The rule generates an alert when an email is blocked after Sanitization process which is not password protected and has a from field meaning its a valid email. More information in terms of details about the blocked attachment, such as the file name and hash, and information about the email, such as the sender and recipient(s). The alert also includes information about the Votiro policy that blocked the attachment and provides a link to additional details about the incident.'
severity: Low
requiredDataConnectors:
- connectorId: Votiro
dataTypes:
- CommonSecurityLog
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- DefenseEvasion
- Impact
- InitialAccess
relevantTechniques:
- T0885
- T1036
- T1027
- T1486
- T1566
query: let Votiro_view = view () {
VotiroEvents
| where sanitizationResult has "Blocked" and passwordProtected == "false" and from =~ "null"
| extend FileWithEmailDetails = strcat_delim(' ', fileName, 'with the hash', SrcFileSHA256, 'was blocked in an email that was sent from user', from, 'to the following recipients', recipients)
| summarize count() by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, tostring(incidentURL), sanitizationResult, LogSeverity
| extend FileHashAlgo = "SHA256", FileHashValue = SrcFileSHA256};Votiro_view
entityMappings:
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: FileHashAlgo
- identifier: Value
columnName: FileHashValue
incidentConfiguration:
createIncident: true
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: File with hash {{SrcFileSHA256}} was blocked
alertDescriptionFormat: Attachment {{FileWithEmailDetails}} by Votiro due to Policy rules, see more detail in the following link {{incidentURL}}
alertTacticsColumnName: sanitizationResult
alertSeverityColumnName: LogSeverity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Let Binding: Votiro_view
let Votiro_view = view () { VotiroEvents | where sanitizationResult has "Blocked" and passwordProtected == "false" and from =~ "null" | extend FileWithEmailDetails = strcat_delim(' ', fileName, 'with the hash', SrcFileSHA256, 'was blocked in an email that was sent from user', from, 'to the following recipients', recipients) | summarize count() by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, tostring(incidentURL), sanitizationResult, LogSeverity | extend FileHashAlgo = "SHA256", FileHashValue = SrcFileSHA256};
Stage 1: source
VotiroEvents
Stage 2: where
where from =~ "null" and passwordProtected == false and sanitizationResult has "Blocked"
Stage 3: extend
extend FileWithEmailDetails
Stage 4: summarize
summarize by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, sanitizationResult, LogSeverity
Stage 5: extend
extend FileHashAlgo, FileHashValue
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
from | eq |
| field:"from" kind:eq value:"null" |
passwordProtected | eq |
| field:"passwordProtected" kind:eq value:"false" |
sanitizationResult | match |
| field:"sanitizationResult" kind:match value:"Blocked" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
FileWithEmailDetails | summarize |
LogSeverity | summarize |
SrcFileSHA256 | summarize |
fileName | summarize |
policyName | summarize |
sanitizationResult | summarize |
FileHashAlgo | extend |
FileHashValue | extend |