Detection rules › Kusto
Kusto rules: 0r
AFD WAF - Path Traversal Attack
#'Identifies a match for a Path Traversal based attack in the AFD WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/www-community/attacks/Path_Traversal'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Privilege Escalation | |
| Discovery |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
id: a4d99328-e4e6-493d-b0d5-57e6f9ddae77
name: AFD WAF - Path Traversal Attack
description: |
'Identifies a match for a Path Traversal based attack in the AFD WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
References: https://owasp.org/www-community/attacks/Path_Traversal'
severity: High
status: Available
requiredDataConnectors:
- connectorId: WAF
dataTypes:
- AzureDiagnostics
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
- Execution
- InitialAccess
- PrivilegeEscalation
- Discovery
relevantTechniques:
- T1548
- T1203
- T1190
- T1548
- T1087
tags:
- Path Traversal
query: |
let Threshold = 3;
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "AnomalyScoring"
| where details_msg_s has "Path Traversal Attack"
| parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *
| project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData
| join kind = inner(
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "Block") on trackingReference_s
| summarize URI_s = make_set(requestUri_s,100), Table = make_set(TableName,100), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TrackingReference = make_set(trackingReference_s,100), Matched_Data = make_set(MatchedData,100), Detail_Data = make_set(details_data_s,100), Detail_Message = make_set(details_msg_s,100), Total_TrackingReference = dcount(trackingReference_s) by clientIP_s, host_s, action_s
| where Total_TrackingReference >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URI_s
- entityType: IP
fieldMappings:
- identifier: Address
columnName: clientIP_s
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let Threshold = 3;
Stage 1: source
AzureDiagnostics
Stage 2: where
| where Category =~ "FrontDoorWebApplicationFirewallLog"
Stage 3: where
| where action_s =~ "AnomalyScoring"
Stage 4: where
| where details_msg_s has "Path Traversal Attack"
Stage 5: parse
| parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *
Stage 6: project
| project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData
Stage 7: join
| join kind = inner(
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "Block") on trackingReference_s
Stage 8: summarize
summarize Detail_Data, Detail_Message, EndTime, Matched_Data, StartTime, Table, Total_TrackingReference, TrackingReference, URI_s by clientIP_s, host_s, action_s
Stage 9: where
where Total_TrackingReference >= 3
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"FrontDoorWebApplicationFirewallLog" |
Total_TrackingReference | ge |
| field:"Total_TrackingReference" kind:ge value:"3" |
action_s | eq |
| field:"action_s" kind:eq |
details_msg_s | match |
| field:"details_msg_s" kind:match value:"Path Traversal Attack" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Detail_Data | summarize |
Detail_Message | summarize |
EndTime | summarize |
Matched_Data | summarize |
StartTime | summarize |
Table | summarize |
Total_TrackingReference | summarize |
TrackingReference | summarize |
URI_s | summarize |
action_s | summarize |
clientIP_s | summarize |
host_s | summarize |
API - Account Takeover
#'42Crunch API protection against account takeover'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Discovery |
Rule Body
id: 25c86f99-0a91-4b7f-88f3-599a008e5ab8
name: API - Account Takeover
description: |
'42Crunch API protection against account takeover'
severity: High
requiredDataConnectors:
- connectorId: 42CrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
- connectorId: FortyTwoCrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: SingleAlert
status: Available
tactics:
- CredentialAccess
- Discovery
relevantTechniques:
- T1110
- T1087
query: |
let firstRec = FortyTwoCrunchAPIProtection
| where TimeGenerated >= ago(5m)
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
| where (UriPath has "/api/register" or UriPath has "/api/reset") and Status == 403;
let recCount = iff((toscalar(firstRec | count) >= 20), 1, 0);
firstRec | take recCount
customDetails:
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Hostname
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InstanceName
version: 3.0.1
kind: Scheduled
Stages and Predicates
let firstRec is inlined into the numbered stages below.
Let Binding: recCount
let recCount = iff((toscalar(firstRec | count) >= 20), 1, 0);
Stages 1 to 4 define let firstRec as the rule's main pipeline source. Stage 5 uses it.
Stage 1: source
FortyTwoCrunchAPIProtection
Stage 2: where
| where TimeGenerated >= ago(5m)
Stage 3: project-away
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
Stage 4: where
| where (UriPath has "/api/register" or UriPath has "/api/reset") and Status == 403
Stage 5: take
firstRec
| take recCount
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Status | eq |
| field:"Status" kind:eq value:"403" |
UriPath | match |
| field:"UriPath" kind:match |
Application Redirect URL Update
#Detects the redirect URL of an app being changed. Applications associated with URLs not controlled by the organization can pose a security risk. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-configuration-changes
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Update application |
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions (Kusto)
- [Entra ID] Application Granted Administrative Permission to Assign Microsoft Entra ID Roles (Kusto)
- Added Credentials to Existing Application (Sigma)
- Added Owner To Application (Sigma)
- Admin promotion after Role Management Application Permission Grant (Kusto)
- App Granted Microsoft Permissions (Sigma)
- App Granted Privileged Delegated Or App Permissions (Sigma)
- Application AppID Uri Configuration Changes (Sigma)
Rule Body
id: a1080fc1-13d1-479b-8340-255f0290d96c
name: Application Redirect URL Update
description: |
'Detects the redirect URL of an app being changed.
Applications associated with URLs not controlled by the organization can pose a security risk.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-configuration-changes'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
AuditLogs
| where Category =~ "ApplicationManagement"
| where Result =~ "success"
| where OperationName =~ 'Update Application'
| where TargetResources has "AppAddress"
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| where TargetResources_modifiedProperties.displayName =~ "AppAddress"
| extend Key = tostring(TargetResources_modifiedProperties.displayName)
| extend NewValue = TargetResources_modifiedProperties.newValue
| extend OldValue = TargetResources_modifiedProperties.oldValue
| where isnotempty(Key) and isnotempty(NewValue)
| project-reorder Key, NewValue, OldValue
| extend NewUrls = extract_all('"Address":([^,]*)', tostring(NewValue))
| extend OldUrls = extract_all('"Address":([^,]*)', tostring(OldValue))
| extend AddedUrls = set_difference(NewUrls, OldUrls)
| where array_length(AddedUrls) > 0
| extend UserAgent = iif(tostring(AdditionalDetails[0].key) == "User-Agent", tostring(AdditionalDetails[0].value), "")
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend AddedBy = iif(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName)
| extend TargetAppName = tostring(TargetResources.displayName)
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| project-reorder TimeGenerated, TargetAppName, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIPAddress, AddedUrls, AddedBy, UserAgent
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: AddedUrls
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAppServicePrincipalId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: InitiatingIPAddress
version: 1.1.1
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Stage 1: source
AuditLogs
Stage 2: where
| where Category =~ "ApplicationManagement"
Stage 3: where
| where Result =~ "success"
Stage 4: where
| where OperationName =~ 'Update Application'
Stage 5: where
| where TargetResources has "AppAddress"
Stage 6: mv-expand
| mv-expand TargetResources
Stage 7: mv-expand
| mv-expand TargetResources.modifiedProperties
Stage 8: where
| where TargetResources_modifiedProperties.displayName =~ "AppAddress"
Stage 9: extend (3 consecutive steps)
| extend Key = tostring(TargetResources_modifiedProperties.displayName)
| extend NewValue = TargetResources_modifiedProperties.newValue
| extend OldValue = TargetResources_modifiedProperties.oldValue
Stage 10: where
| where isnotempty(Key) and isnotempty(NewValue)
Stage 11: project-reorder
| project-reorder Key, NewValue, OldValue
Stage 12: extend (3 consecutive steps)
| extend NewUrls = extract_all('"Address":([^,]*)', tostring(NewValue))
| extend OldUrls = extract_all('"Address":([^,]*)', tostring(OldValue))
| extend AddedUrls = set_difference(NewUrls, OldUrls)
Stage 13: where
| where array_length(AddedUrls) > 0
Stage 14: extend (9 consecutive steps)
| extend UserAgent = iif(tostring(AdditionalDetails[0].key) == "User-Agent", tostring(AdditionalDetails[0].value), "")
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend AddedBy = iif(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName)
| extend TargetAppName = tostring(TargetResources.displayName)
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
Stage 15: project-reorder
| project-reorder TimeGenerated, TargetAppName, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIPAddress, AddedUrls, AddedBy, UserAgent
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"ApplicationManagement" |
Key | is_not_null | field:"Key" kind:is_not_null | |
NewValue | is_not_null | field:"Details" kind:is_not_null | |
OperationName | eq |
| field:"OperationName" kind:eq value:"Update Application" |
Result | eq |
| field:"Result" kind:eq value:"success" |
TargetResources | match |
| field:"azure_ad::target_resources" kind:match value:"AppAddress" |
TargetResources_modifiedProperties.displayName | eq |
| field:"TargetResources_modifiedProperties.displayName" kind:eq value:"AppAddress" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Key | extend |
NewValue | extend |
OldValue | extend |
NewUrls | extend |
OldUrls | extend |
AddedUrls | extend |
UserAgent | extend |
InitiatingAppName | extend |
InitiatingAppServicePrincipalId | extend |
InitiatingUserPrincipalName | extend |
InitiatingAadUserId | extend |
InitiatingIPAddress | extend |
AddedBy | extend |
TargetAppName | extend |
InitiatingAccountName | extend |
InitiatingAccountUPNSuffix | extend |
Caramel Tsunami Actor IOC - July 2021
#Identifies a match across IOC's related to an actor tracked by Microsoft as Caramel Tsunami
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
id: 066395ac-ef91-4993-8bf6-25c61ab0ca5a
name: Caramel Tsunami Actor IOC - July 2021
description: |
'Identifies a match across IOC's related to an actor tracked by Microsoft as Caramel Tsunami'
severity: High
status: Available
requiredDataConnectors:
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1546
tags:
- Caramel Tsunami
query: |
let iocs = externaldata(DateAdded:string,IoC:string,Type:string,TLP:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/SOURGUM.csv"] with (format="csv", ignoreFirstRecord=True);
let file_path1 = (iocs | where Type =~ "filepath1" | project IoC);
let file_path2 = (iocs | where Type =~ "filepath2" | project IoC);
let file_path3 = (iocs | where Type =~ "filepath3" | project IoC);
let reg_key = (iocs | where Type =~ "regkey" | project IoC);
WindowsEvent
| where EventID == 4688 and (EventData has_any (file_path1) or EventData has_any (file_path2) or EventData has_any (file_path3) or EventData has_any ('reg add') or EventData has_any (reg_key) )
| extend CommandLine = tostring(EventData.CommandLine)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| where (CommandLine has_any (file_path1)) or
(CommandLine has_any (file_path3)) or
(CommandLine has 'reg add' and CommandLine has_any (reg_key) and CommandLine has_any (file_path2)) or
(NewProcessName has_any (file_path1)) or
(NewProcessName has_any (file_path3)) or
(ParentProcessName has_any (file_path1)) or
(ParentProcessName has_any (file_path3))
| extend Account = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
| extend NewProcessId = tostring(EventData.NewProcessId)
| extend IP = tostring(EventData.IpAddress)
| project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type, IP
| extend Alert = 'SOURGUM IOC detected'
| extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Account
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: AccountNTDomain
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IP
- entityType: Process
fieldMappings:
- identifier: ProcessId
columnName: NewProcessId
version: 1.0.4
kind: Scheduled
Stages and Predicates
Let Binding: iocs
let iocs = externaldata(DateAdded:string,IoC:string,Type:string,TLP:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/SOURGUM.csv"] with (format="csv", ignoreFirstRecord=True);
Let Binding: file_path1
let file_path1 = (iocs | where Type =~ "filepath1" | project IoC);
Let Binding: file_path2
let file_path2 = (iocs | where Type =~ "filepath2" | project IoC);
Let Binding: file_path3
let file_path3 = (iocs | where Type =~ "filepath3" | project IoC);
Let Binding: reg_key
let reg_key = (iocs | where Type =~ "regkey" | project IoC);
Stage 1: source
WindowsEvent
Stage 2: where
| where EventID == 4688 and (EventData has_any (file_path1) or EventData has_any (file_path2) or EventData has_any (file_path3) or EventData has_any ('reg add') or EventData has_any (reg_key) )
Stage 3: extend (3 consecutive steps)
| extend CommandLine = tostring(EventData.CommandLine)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
Stage 4: where
| where (CommandLine has_any (file_path1)) or
(CommandLine has_any (file_path3)) or
(CommandLine has 'reg add' and CommandLine has_any (reg_key) and CommandLine has_any (file_path2)) or
(NewProcessName has_any (file_path1)) or
(NewProcessName has_any (file_path3)) or
(ParentProcessName has_any (file_path1)) or
(ParentProcessName has_any (file_path3))
Stage 5: extend (3 consecutive steps)
| extend Account = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
| extend NewProcessId = tostring(EventData.NewProcessId)
| extend IP = tostring(EventData.IpAddress)
Stage 6: project
| project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type, IP
Stage 7: extend (4 consecutive steps)
| extend Alert = 'SOURGUM IOC detected'
| extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | match |
| field:"CommandLine" kind:match |
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
NewProcessName | match |
| field:"NewProcessName" kind:match |
ParentProcessName | match |
| field:"ParentProcessName" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
Computer | project |
IP | project |
NewProcessId | project |
NewProcessName | project |
ParentProcessName | project |
TimeGenerated | project |
Type | project |
Alert | extend |
AccountNTDomain | extend |
AccountName | extend |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
CTERA Mass Permissions Changes Detection Analytic
#This analytic rule detects and alerts when access denied operations generated by the CTERA Edge Filer goes over a predefined threshold
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
id: 90502ac9-19a2-41f0-ba81-e352de90b61b
name: CTERA Mass Permissions Changes Detection Analytic
description: 'This analytic rule detects and alerts when access denied operations generated by the CTERA Edge Filer goes over a predefined threshold'
kind: Scheduled
severity: High
status: Available
requiredDataConnectors:
- connectorId: CTERA
dataTypes:
- Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 5000
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1068
query: |
Syslog
| where ProcessName == 'gw-audit'
| extend
TenantName = extract("\"vportal\":\"([^\"]*)\"", 1, SyslogMessage),
UserName = extract("user=([^|]*)", 1, SyslogMessage),
Operation = extract("op=([^|]*)", 1, SyslogMessage),
EdgeFiler = extract("\"client\":\"([^\"]*)\"", 1, SyslogMessage),
RootPath = extract("rootPath=([^|]*)", 1, SyslogMessage),
Share = extract("share=([^|]*)", 1, SyslogMessage),
LocalPath = extract("path=([^|]*)", 1, SyslogMessage),
Timestamp = todatetime(extract("\"@timestamp\":\"([^\"]*)\"", 1, SyslogMessage))
| where Operation in ('ACLAdded', 'ACLDeleted', 'ACLProtectionAdded','chown', 'setsd', 'ACLProtectionDeleted', 'ACEChanged', 'setdacl')
| summarize Count = count() by UserName, bin(Timestamp, 5m)
| where Count > 5000
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertnameFormat: 'CTERA Batch Access Denied Detection'
alertDescriptionFormat: >
Detected {{Count}} denied access attempts by user {{UserName}} on Edge Filer
{{EdgeFiler}} within 5 minutes. Please investigate unauthorized access attempts or misconfigurations.
customDetails:
TenantName: TenantName
UserName: UserName
EdgeFiler: EdgeFiler
RootPath: RootPath
Share: Share
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserName
- entityType: File
fieldMappings:
- identifier: Name
columnName: EdgeFiler
suppressionDuration: PT5H
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 1: source
Syslog
Stage 2: where
| where ProcessName == 'gw-audit'
Stage 3: extend
| extend
TenantName = extract("\"vportal\":\"([^\"]*)\"", 1, SyslogMessage),
UserName = extract("user=([^|]*)", 1, SyslogMessage),
Operation = extract("op=([^|]*)", 1, SyslogMessage),
EdgeFiler = extract("\"client\":\"([^\"]*)\"", 1, SyslogMessage),
RootPath = extract("rootPath=([^|]*)", 1, SyslogMessage),
Share = extract("share=([^|]*)", 1, SyslogMessage),
LocalPath = extract("path=([^|]*)", 1, SyslogMessage),
Timestamp = todatetime(extract("\"@timestamp\":\"([^\"]*)\"", 1, SyslogMessage))
Stage 4: where
| where Operation in ('ACLAdded', 'ACLDeleted', 'ACLProtectionAdded','chown', 'setsd', 'ACLProtectionDeleted', 'ACEChanged', 'setdacl')
Stage 5: summarize
| summarize Count = count() by UserName, bin(Timestamp, 5m)
Stage 6: where
| where Count > 5000
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Count | gt |
| field:"Count" kind:gt value:"5000" |
Operation | in |
| field:"Operation" kind:in |
ProcessName | eq |
| field:"process_name" kind:eq value:"gw-audit" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Count | summarize |
UserName | summarize |
Datawiza - massive errors detected
#"This rule is designed to identify when the system is experiencing abnormal errors."
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: "ddee1398-cf0b-46af-b583-78c3c29156dc"
name: "Datawiza - massive errors detected"
description: |
"This rule is designed to identify when the system is experiencing abnormal errors."
severity: Medium
requiredDataConnectors:
- connectorId: DatawizaDapSolution
dataTypes:
- datawizaserveraccess_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1082
query: |
let timeFrame = 10m;
datawizaserveraccess_CL
| where TimeGenerated between (ago(timeFrame) .. now())
and Status_d >= 500
| summarize Count = count()
| where Count > 100
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: 'Datawiza Massive Error Detection'
alertDescriptionFormat: >
Detected {{Count}} errors within 10 minutes. Please investigate unauthorized access attempts or misconfigurations.
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let timeFrame = 10m;
Stage 1: source
datawizaserveraccess_CL
Stage 2: where
| where TimeGenerated between (ago(timeFrame) .. now())
and Status_d >= 500
Stage 3: summarize
| summarize Count = count()
Stage 4: where
| where Count > 100
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Count | gt |
| field:"Count" kind:gt value:"100" |
Status_d | ge |
| field:"Status_d" kind:ge value:"500" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Count | summarize |
Detect first time Azure Custom Script or Run Command deployment
#This detection rule flags using UEBA of Defender XDR and Microsoft Sentinel if it is the first time that an account is deploying Custom Scripts or Run Commands on Azure and Azure Arc machines. Since UEBA uses a baseline of 180 days, it might indicate that an account is being abused to compormise Azure or Azure Arc machines.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Lateral Movement |
References
Rule Body
BehaviorAnalytics
| where TimeGenerated > ago(1h)
| extend ActivityInsights = parse_json(ActivityInsights)
| where ActivityInsights.EventMessage has_any ('runCommand/action', 'extensions/write')
| where ActivityInsights.FirstTimeUserPerformedAction == "True"
Stages and Predicates
Stage 1: source
BehaviorAnalytics
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: extend
| extend ActivityInsights = parse_json(ActivityInsights)
Stage 4: where
| where ActivityInsights.EventMessage has_any ('runCommand/action', 'extensions/write')
Stage 5: where
| where ActivityInsights.FirstTimeUserPerformedAction == "True"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActivityInsights.EventMessage | match |
| field:"ActivityInsights.EventMessage" kind:match |
ActivityInsights.FirstTimeUserPerformedAction | eq |
| field:"ActivityInsights.FirstTimeUserPerformedAction" kind:eq value:"True" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityInsights | extend |
McAfee ePO - File added to exceptions
#Detects when file was added to exception list on a host.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth | |
| Defense Impairment |
Telemetry Coverage
Rule Body
id: b9d9fdfe-bc17-45ce-a70d-67a5cfd119f4
name: McAfee ePO - File added to exceptions
description: |
'Detects when file was added to exception list on a host.'
severity: Medium
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
- T1070
query: |
McAfeeEPOEvent
| where EventId in ('1029', '2005', '2015')
| project DvcIpAddr, DstFileName
| extend IPCustomEntity = DvcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
McAfeeEPOEvent
Stage 2: where
| where EventId in ('1029', '2005', '2015')
Stage 3: project
| project DvcIpAddr, DstFileName
Stage 4: extend
| extend IPCustomEntity = DvcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | in |
| field:"EventId" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstFileName | project |
DvcIpAddr | project |
IPCustomEntity | extend |
Potential communication with a Domain Generation Algorithm (DGA) based hostname (ASIM Web Session schema)
#'This rule identifies communication with hosts that have a domain name that might have been generated by a Domain Generation Algorithm (DGA). DGAs are used by malware to generate rendezvous points that are difficult to predict in advance. This detection uses the top 1 million domain names to build a model of what normal domains look like nad uses the model to identify domains that may have been randomly generated by an algorithm. You can modify the triThreshold and dgaLengthThreshold query parameters to change Analytic Rule sensitivity. The higher the numbers, the less noisy the rule is. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM WebSession schema (ASIM WebSession Schema)'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 9176b18f-a946-42c6-a2f6-0f6d17cd6a8a
name: Potential communication with a Domain Generation Algorithm (DGA) based hostname (ASIM Web Session schema)
description: |
'This rule identifies communication with hosts that have a domain name that might have been generated by a Domain Generation Algorithm (DGA).
DGAs are used by malware to generate rendezvous points that are difficult to predict in advance. This detection uses the top 1 million domain names to build a model of what normal domains look like nad uses the model to identify domains that may have been randomly generated by an algorithm. You can modify the triThreshold and dgaLengthThreshold query parameters to change Analytic Rule sensitivity. The higher the numbers, the less noisy the rule is.
This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM WebSession schema (ASIM WebSession Schema)'
severity: Medium
requiredDataConnectors:
- connectorId: SquidProxy
dataTypes:
- SquidProxy_CL
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
tags:
- ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/CommonSecurityLog/MultiVendor-PossibleDGAContacts.yaml
version: 1.0.0
- Schema: ASIMWebSession
SchemaVersion: 0.2.0
relevantTechniques:
- T1568
query: |
let triThreshold = 500;
let querystarttime = 6h;
let dgaLengthThreshold = 8;
// fetch the cisco umbrella top 1M domains
let top1M = (externaldata (Position:int, Domain:string) [@"http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip"] with (format="csv", zipPattern="*.csv"));
// extract tri grams that are above our threshold - i.e. are common
let triBaseline = top1M
| extend Domain = tolower(extract("([^.]*).{0,7}$", 1, Domain))
| extend AllTriGrams = array_concat(extract_all("(...)", Domain), extract_all("(...)", substring(Domain, 1)), extract_all("(...)", substring(Domain, 2)))
| mvexpand Trigram=AllTriGrams to typeof(string)
| summarize triCount=count() by Trigram
| sort by triCount desc
| where triCount > triThreshold
| distinct Trigram;
// collect domain information from common security log, filter and extract the DGA candidate and its trigrams
let allDataSummarized = _Im_WebSession
| where isnotempty(Url)
| extend Name = tolower(tostring(parse_url(Url)["Host"]))
| summarize NameCount=count() by Name
| where Name has "."
| where Name !endswith ".home" and Name !endswith ".lan"
// extract DGA candidate
| extend DGADomain = extract("([^.]*).{0,7}$", 1, Name)
| where strlen(DGADomain) > dgaLengthThreshold
// throw out domains with number in them
| where DGADomain matches regex "^[A-Za-z]{0,}$"
// extract the tri grams from summarized data
| extend AllTriGrams = array_concat(extract_all("(...)", DGADomain), extract_all("(...)", substring(DGADomain, 1)), extract_all("(...)", substring(DGADomain, 2)));
// throw out domains that have repeating tri's and/or >=3 repeating letters
let nonRepeatingTris = allDataSummarized
| join kind=leftanti
(
allDataSummarized
| mvexpand AllTriGrams
| summarize count() by tostring(AllTriGrams), DGADomain
| where count_ > 1
| distinct DGADomain
)
on DGADomain;
// find domains that do not have a common tri in the baseline
let dataWithRareTris = nonRepeatingTris
| join kind=leftanti
(
nonRepeatingTris
| mvexpand AllTriGrams
| extend Trigram = tostring(AllTriGrams)
| distinct Trigram, DGADomain
| join kind=inner
(
triBaseline
)
on Trigram
| distinct DGADomain
)
on DGADomain;
dataWithRareTris
// join DGAs back on connection data
| join kind=inner
(
_Im_WebSession
| where isnotempty(Url)
| extend Url = tolower(Url)
| summarize arg_max(TimeGenerated, EventVendor, SrcIpAddr) by Url
| extend Name=tostring(parse_url(Url)["Host"])
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated) by Name, SrcIpAddr, Url
)
on Name
| project StartTime, EndTime, Name, DGADomain, SrcIpAddr, Url, NameCount
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
alertDetailsOverride:
alertDisplayNameFormat: Potential communication from {{SrcIpAddr}} with a Domain Generation Algorithm (DGA) based host {{Name}}
alertDescriptionFormat: A client with address {{SrcIpAddr}} communicated with host {{Name}} that have a domain name that might have been generated by a Domain Generation Algorithm (DGA), identified by the pattern {{DGADomain}}. DGAs are used by malware to generate rendezvous points that are difficult to predict in advance. This detection uses the top 1 million domain names to build a model of what normal domains look like and uses the model to identify domains that may have been randomly generated by an algorithm.
customDetails:
DGAPattern: DGADomain
NameCount: NameCount
version: 1.1.5
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Yaron
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Parameters
let triThreshold = 500;
let querystarttime = 6h;
let dgaLengthThreshold = 8;
let allDataSummarized, let nonRepeatingTris and let dataWithRareTris are inlined into the numbered stages below.
Let Binding: top1M
let top1M = (externaldata (Position:int, Domain:string) [@"http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip"] with (format="csv", zipPattern="*.csv"));
Let Binding: triBaseline
let triBaseline = top1M
| extend Domain = tolower(extract("([^.]*).{0,7}$", 1, Domain))
| extend AllTriGrams = array_concat(extract_all("(...)", Domain), extract_all("(...)", substring(Domain, 1)), extract_all("(...)", substring(Domain, 2)))
| mvexpand Trigram=AllTriGrams to typeof(string)
| summarize triCount=count() by Trigram
| sort by triCount desc
| where triCount > triThreshold
| distinct Trigram;
Stages 1 to 12 define let dataWithRareTris as the rule's main pipeline source. Stages 13 to 14 use it.
Stage 1: source
_Im_WebSession
Stage 2: where
where isnotempty(Url)
Stage 3: extend
extend Name
Stage 4: summarize
summarize NameCount by Name
Stage 5: where
where Name has "."
Stage 6: where
where not (Name endswith ".home") and not (Name endswith ".lan")
Stage 7: extend
extend DGADomain
Stage 8: where
where DGADomain > 8
Stage 9: where
where DGADomain matches regex "^[A-Za-z]{0,}$"
Stage 10: extend
extend AllTriGrams
Stage 11: join (negated)
join kind=leftanti (allDataSummarized) on DGADomain
Stage 12: join (negated)
join kind=leftanti (nonRepeatingTris) on DGADomain
Stage 13: join
dataWithRareTris
| join kind=inner
(
_Im_WebSession
| where isnotempty(Url)
| extend Url = tolower(Url)
| summarize arg_max(TimeGenerated, EventVendor, SrcIpAddr) by Url
| extend Name=tostring(parse_url(Url)["Host"])
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated) by Name, SrcIpAddr, Url
)
on Name
Stage 14: project
project DGADomain, EndTime, Name, NameCount, SrcIpAddr, StartTime, Url
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DGADomain | gt |
| field:"DGADomain" kind:gt value:"8" |
DGADomain | regex_match |
| field:"DGADomain" kind:regex_match value:"^[A-Za-z]{0,}$" |
Name | ends_with |
| field:"Name" kind:ends_with |
Name | match |
| field:"Name" kind:match value:"." |
Url | is_not_null | field:"Url" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Name | ends_with | .home | excludes:Name field:"Name" value:".home" |
Name | ends_with | .lan | excludes:Name field:"Name" value:".lan" |
DGADomain | gt | 8 | excludes:DGADomain field:"DGADomain" value:"8" |
DGADomain | regex_match | ^[A-Za-z]{0,}$ | excludes:DGADomain field:"DGADomain" value:"^[A-Za-z]{0,}$" |
Name | match | . | excludes:Name field:"Name" value:"." |
Url | is_not_null | excludes:Url | |
count_ | gt | 1 | excludes:count_ field:"count_" value:"1" |
triCount | gt | 500 | excludes:triCount field:"triCount" value:"500" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DGADomain | project |
EndTime | project |
Name | project |
NameCount | project |
SrcIpAddr | project |
StartTime | project |
Url | project |
SailPointIdentityNowAlertForTriggers
#'Create alerts for SailPoint IdentityNow Event Trigger Service.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Collection |
Rule Body
id: 08330c3d-487e-4f5e-a539-1e7d06dea786
name: SailPointIdentityNowAlertForTriggers
description: |
'Create alerts for SailPoint IdentityNow Event Trigger Service.'
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: SailPointIdentityNow
dataTypes:
- SailPointIDN_Events_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Collection
relevantTechniques:
- T1133
- T1005
query: |
declare query_parameters(lbperiod:timespan = 14d, triggerId:string = "idn:aggregation-accounts-collected");
SailPointIDN_Triggers_CL
| where TimeGenerated > ago(lbperiod)
| where Metadata_triggerId_s == triggerId
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Metadata_triggertId_s
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
declare query_parameters(lbperiod:timespan = 14d, triggerId:string = "idn:aggregation-accounts-collected");
SailPointIDN_Triggers_CL
Stage 2: where
| where TimeGenerated > ago(lbperiod)
Stage 3: where
| where Metadata_triggerId_s == triggerId
Semperis DSP Recent sIDHistory changes on AD objects
#'This indicator detects any recent changes to sIDHistory on AD objects, including changes to non-privileged accounts where privileged SIDs are added.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Rule Body
id: 64796da3-6383-4de2-9c97-866c83c459ae
name: Semperis DSP Recent sIDHistory changes on AD objects
description: |
'This indicator detects any recent changes to sIDHistory on AD objects, including changes to non-privileged accounts where privileged SIDs are added.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SemperisDSP
dataTypes:
- dsp_parser
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- Persistence
relevantTechniques:
- T1098
query: |
dsp_parser
| where EventID == 9212
| where SecurityIndicatorName == "Recent sIDHistory changes on objects"
| extend 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
version: 2.0.7
kind: Scheduled
Stages and Predicates
Stage 1: source
dsp_parser
Stage 2: where
| where EventID == 9212
Stage 3: where
| where SecurityIndicatorName == "Recent sIDHistory changes on objects"
Stage 4: extend
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | eq |
| field:"EventID" kind:eq value:"9212" |
SecurityIndicatorName | eq |
| field:"SecurityIndicatorName" kind:eq value:"Recent sIDHistory changes on objects" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
Tailscale Premium: Mass fan-out from single node
#Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery | |
| Lateral Movement |
Rule Body
id: f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c
name: "Tailscale Premium: Mass fan-out from single node"
description: |
Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.
severity: High
status: Available
requiredDataConnectors:
- connectorId: TailscalePremiumCCF
dataTypes:
- Tailscale_Network_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
- LateralMovement
relevantTechniques:
- T1018
- T1021
- T1046
query: |
let dstThreshold = 25;
Tailscale_Network_CL
| where TimeGenerated > ago(15m)
| where HasVirtualTraffic
| mv-expand t = VirtualTraffic
| extend Src = tostring(t.src), Dst = tostring(t.dst)
| summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src
| where UniqueDestinations >= dstThreshold
| order by UniqueDestinations desc
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SrcNodeName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SrcUser
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Src
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0
Stages and Predicates
Parameters
let dstThreshold = 25;
Stage 1: source
Tailscale_Network_CL
Stage 2: where
| where TimeGenerated > ago(15m)
Stage 3: where
| where HasVirtualTraffic
Stage 4: mv-expand
| mv-expand t = VirtualTraffic
Stage 5: extend
| extend Src = tostring(t.src), Dst = tostring(t.dst)
Stage 6: summarize
| summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src
Stage 7: where
| where UniqueDestinations >= dstThreshold
Stage 8: sort
| order by UniqueDestinations desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
HasVirtualTraffic | eq |
| field:"HasVirtualTraffic" kind:eq value:"true" |
UniqueDestinations | ge |
| field:"UniqueDestinations" kind:ge value:"25" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
NodeId | summarize |
Src | summarize |
SrcNodeName | summarize |
SrcOs | summarize |
SrcTags | summarize |
SrcUser | summarize |
TopDestinations | summarize |
UniqueDestinations | summarize |
Tape Server Deleted
#Detects when a tape server is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.
Rule Body
id: fda79d00-9e6a-46f8-b7a2-2bdccd5f598e
name: Tape Server Deleted
description: Detects when a tape server is deleted from Veeam Backup & Replication.
This might indicate unauthorized removal of critical components.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 28800\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n UserName = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 28800
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
UserName = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"28800" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
UserName | project |
Ubiquiti - connection to non-corporate DNS server
#'Detects connections to non-corporate DNS servers.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | |
| Exfiltration |
Rule Body
id: fe232837-9bdc-4e2b-8c08-cdac2610eed3
name: Ubiquiti - connection to non-corporate DNS server
description: |
'Detects connections to non-corporate DNS servers.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
dataTypes:
- Ubiquiti_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- Exfiltration
relevantTechniques:
- T1572
- T1041
query: |
let corp_dns = dynamic(['127.0.0.2']);
UbiquitiAuditEvent
| where DstPortNumber == '53'
| where DstIpAddr !in (corp_dns)
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let corp_dns = dynamic(['127.0.0.2']);
Stage 1: source
UbiquitiAuditEvent
Stage 2: where
| where DstPortNumber == '53'
Stage 3: where
| where DstIpAddr !in (corp_dns)
Stage 4: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstPortNumber | eq |
| field:"DstPortNumber" kind:eq value:"53" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstIpAddr | eq | corp_dns | excludes:DstIpAddr field:"DstIpAddr" value:"corp_dns" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |