Detection rules › Kusto
Kusto rules: 1l
AIShield - Tabular classification AI Model Evasion high suspicious vulnerability detection
#'This alert creates an incident when Tabular classification AI Model Evasion high suspicious, high severity vulnerability detected from the AIShield.'
Rule Body
id: 669680fb-91e5-4cbd-9eb6-e5352e0f8af0
name: AIShield - Tabular classification AI Model Evasion high suspicious vulnerability detection
description: |
'This alert creates an incident when Tabular classification AI Model Evasion high suspicious, high severity vulnerability detected from the AIShield.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- AIShield
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
AIShield
| where Message has 'Tabular Classification AI Model Evasion Attack Identified'
| where Severity =~ 'High'
| where SuspiciousLevel =~ 'high suspicious attack'
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceName
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: AIShield - Tabular Classification AI Model Evasion high suspicious vulnerability detected.
alertDescriptionFormat: |
This query detects Tabular Classification AI Model Evasion high suspicious, high severity alert from AIShield generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
alertTacticsColumnName: null
alertSeverityColumnName: Severity
version: 1.0.4
kind: Scheduled
Stages and Predicates
Stage 1: source
AIShield
Stage 2: where
| where Message has 'Tabular Classification AI Model Evasion Attack Identified'
Stage 3: where
| where Severity =~ 'High'
Stage 4: where
| where SuspiciousLevel =~ 'high suspicious attack'
Stage 5: extend
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Message | match |
| field:"Message" kind:match value:"Tabular Classification AI Model Evasion Attack Identified" |
Severity | eq |
| field:"Severity" kind:eq value:"High" |
SuspiciousLevel | eq |
| field:"SuspiciousLevel" kind:eq value:"high suspicious attack" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostName | extend |
NTDomain | extend |
API - Kiterunner detection
#'42Crunch API protection against Kiterunner enumeration'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Discovery |
Rule Body
id: 421b38ec-4295-4aed-8299-c92e268ad663
name: API - Kiterunner detection
description: |
'42Crunch API protection against Kiterunner enumeration'
severity: Medium
requiredDataConnectors:
- connectorId: 42CrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
- connectorId: FortyTwoCrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: SingleAlert
status: Available
tactics:
- Reconnaissance
- Discovery
relevantTechniques:
- T1595
- T1580
- T1083
query: |
let discoveryRecords = FortyTwoCrunchAPIProtection
| where TimeGenerated >= ago(5m)
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
| where Status == 404 and ErrorMessage startswith "path not defined in OAS";
let discoveryCnt = (toscalar(discoveryRecords | count));
let recCount = iff((discoveryCnt > 500), 1, 0);
discoveryRecords | top recCount by Timestamp desc
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
Parameters
let recCount = iff((discoveryCnt > 500), 1, 0);
let discoveryRecords is inlined into the numbered stages below.
Let Binding: discoveryCnt
let discoveryCnt = (toscalar(discoveryRecords | count));
Stages 1 to 4 define let discoveryRecords 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 Status == 404 and ErrorMessage startswith "path not defined in OAS"
Stage 5: top
discoveryRecords
| top recCount by Timestamp desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ErrorMessage | starts_with |
| field:"aws::errorMessage" kind:starts_with value:"path not defined in OAS" |
Status | eq |
| field:"Status" kind:eq value:"404" |
Bitglass - The SmartEdge endpoint agent was uninstalled
#'Detects when SmartEdge endpoint agent was uninstalled.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule Body
id: 40f69a27-8c68-4c8c-bb7c-7eb0f0a8a1fa
name: Bitglass - The SmartEdge endpoint agent was uninstalled
description: |
'Detects when SmartEdge endpoint agent was uninstalled.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Bitglass
dataTypes:
- Bitglass
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1070
query: |
Bitglass
| where EventType =~ 'access'
| where EventResultDetails startswith 'The SmartEdge endpoint agent was uninstalled'
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Bitglass
Stage 2: where
| where EventType =~ 'access'
Stage 3: where
| where EventResultDetails startswith 'The SmartEdge endpoint agent was uninstalled'
Stage 4: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventResultDetails | starts_with |
| field:"EventResultDetails" kind:starts_with value:"The SmartEdge endpoint agent was uninstalled" |
EventType | eq |
| field:"EventType" kind:eq value:"access" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
BloodHound Attack Path Finding - Non-Tier Zero Principal Can Grant Tier Zero App Roles
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
BloodHound Attack Path Finding - Non-Tier Zero Principal Can Grant Tier Zero App Roles
Rule Body
id: 8e1df707-a242-42ba-a0aa-68b61f1e620d
name: BloodHound Attack Path Finding - Non-Tier Zero Principal Can Grant Tier Zero
App Roles
description: BloodHound Attack Path Finding - Non-Tier Zero Principal Can Grant Tier
Zero App Roles
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"AzureT0MGGrantAppRoles\""
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "AzureT0MGGrantAppRoles"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"AzureT0MGGrantAppRoles" |
Cisco Duo - Admin user created
#'Detects when new admin user is created.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Rule Body
id: 0724cb01-4866-483d-a149-eb400fe1daa8
name: Cisco Duo - Admin user created
description: |
'Detects when new admin user is created.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CiscoDuoActivityConnectorDefinition
dataTypes:
- DuoActivity_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1078
query: |
DuoActivity_CL
| where ActionName =~ "admin_create"
| extend AccountCustomEntity = TargetName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
DuoActivity_CL
Stage 2: where
| where ActionName =~ "admin_create"
Stage 3: extend
| extend AccountCustomEntity = TargetName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionName | eq |
| field:"ActionName" kind:eq value:"admin_create" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
CiscoISE - Log files deleted
#'Detects log file deleting activity.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Rule Body
id: 21d3be4c-6088-4e76-b6eb-d25479019cb9
name: CiscoISE - Log files deleted
description: |
'Detects log file deleting activity.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1070.004
query: |
let lbtime = 5m;
CiscoISEEvent
| where TimeGenerated > ago(lbtime)
| where EventId in ('58015', '59101', '59102')
| project TimeGenerated, DvcHostname, DvcIpAddr, DstUserName
| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = DvcIpAddr
| extend AccountCustomEntity = DstUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let lbtime = 5m;
Stage 1: source
CiscoISEEvent
Stage 2: where
| where TimeGenerated > ago(lbtime)
Stage 3: where
| where EventId in ('58015', '59101', '59102')
Stage 4: project
| project TimeGenerated, DvcHostname, DvcIpAddr, DstUserName
Stage 5: extend (3 consecutive steps)
| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = DvcIpAddr
| extend AccountCustomEntity = DstUserName
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 |
|---|---|
DstUserName | project |
DvcHostname | project |
DvcIpAddr | project |
TimeGenerated | project |
HostCustomEntity | extend |
IPCustomEntity | extend |
AccountCustomEntity | extend |
Cloudflare - Empty user agent
#'Detects requests where user agent is empty.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 729c6d21-fad9-4a6a-9c7f-482393c95957
name: Cloudflare - Empty user agent
description: |
'Detects requests where user agent is empty.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CloudflareDataConnector
dataTypes:
- Cloudflare
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
Cloudflare
| where isempty(HttpUserAgentOriginal)
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
Cloudflare
Stage 2: where
| where isempty(HttpUserAgentOriginal)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
HttpUserAgentOriginal | is_null | field:"HttpUserAgentOriginal" kind:is_null |
CYFIRMA - High severity Malicious Phishing Network Indicators - Block Recommended Rule
#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 analytics rule identifies network-based indicators such as URLs, IP addresses, and domains related to phishing campaigns, as reported by CYFIRMA threat intelligence. These indicators are flagged with a recommended action to block and are categorized under the 'Phishing' role. Such infrastructure is often used to deliver phishing emails, host fake login portals, or redirect victims to credential-harvesting pages. Blocking these indicators proactively helps prevent user compromise and data theft."
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Credential Access | |
| Exfiltration |
Rule Body
id: 6f053867-dbd8-4755-924d-577e3db7f5a6
name: CYFIRMA - High severity Malicious Phishing Network Indicators - Block Recommended Rule
description: |
"This analytics rule identifies network-based indicators such as URLs, IP addresses, and domains related to phishing campaigns, as reported by CYFIRMA threat intelligence.
These indicators are flagged with a recommended action to block and are categorized under the 'Phishing' role.
Such infrastructure is often used to deliver phishing emails, host fake login portals, or redirect victims to credential-harvesting pages.
Blocking these indicators proactively helps prevent user compromise and data theft."
version: 1.0.1
kind: Scheduled
severity: High
enabled: false
requiredDataConnectors:
- connectorId: CyfirmaCyberIntelligenceDC
dataTypes:
- CyfirmaIndicators_CL
query: |
//Malicious Phishing Network Indicators - Block Recommended
let timeFrame= 5m;
CyfirmaIndicators_CL
| where ConfidenceScore >= 80
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Phishing'
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
| mv-expand extensionKeys
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
- InitialAccess
- Execution
- CredentialAccess
- Exfiltration
relevantTechniques:
- T1566
- T1204
- T1556
- T1110
- T1041
- T1566.001
- T1566.002
- T1204.001
- T1556.002
- T1110.003
alertDetailsOverride:
alertDisplayNameFormat: "High-Confidence Malicious Phishing Network Indicators - Block Recommended - {{name}} "
alertDescriptionFormat: "{{Description}} - {{name}} "
alertDynamicProperties:
- alertProperty: ProductName
value: ProductName
- alertProperty: ProviderName
value: ProviderName
customDetails:
ThreatActors: ThreatActors
Sources: Sources
RecommendedActions: RecommendedActions
Roles: Roles
Country: Country
Description: Description
ConfidenceScore: ConfidenceScore
SecurityVendors: SecurityVendors
IndicatorID: IndicatorID
Created: created
Modified: modified
ValidFrom: valid_from
Tags: Tags
ThreatType: ThreatType
TimeGenerated: TimeGenerated
IPAbuse: IPAbuse
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv4
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv6
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Parameters
let timeFrame = 5m;
Stage 1: source
CyfirmaIndicators_CL
Stage 2: where
| where ConfidenceScore >= 80
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Phishing'
Stage 3: extend (6 consecutive steps)
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
Stage 4: mv-expand
| mv-expand extensionKeys
Stage 5: extend (4 consecutive steps)
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
Stage 6: project
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ConfidenceScore | ge |
| field:"ConfidenceScore" kind:ge value:"80" |
RecommendedActions | match |
| field:"RecommendedActions" kind:match value:"Block" |
Roles | match |
| field:"Roles" kind:match value:"Phishing" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
pattern | contains | file:hashes | excludes:pattern field:"pattern" value:"file:hashes" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ConfidenceScore | project |
Country | project |
Description | project |
Domain | project |
IPAbuse | project |
IPv4 | project |
IPv6 | project |
IndicatorID | project |
ProductName | project |
ProviderName | project |
RecommendedActions | project |
Roles | project |
SecurityVendors | project |
Sources | project |
Tags | project |
ThreatActors | project |
ThreatType | project |
TimeGenerated | project |
URL | project |
created | project |
modified | project |
name | project |
valid_from | project |
Excessive NXDOMAIN DNS Queries (ASIM DNS Schema)
#This creates an incident in the event a client generates excessive amounts of DNS queries for non-existent domains. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM DNS schema
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 22: DNSEvent (DNS query) |
Rule Body
id: c3b11fb2-9201-4844-b7b9-6b7bf6d9b851
name: Excessive NXDOMAIN DNS Queries (ASIM DNS Schema)
description: |
'This creates an incident in the event a client generates excessive amounts of DNS queries for non-existent domains.
This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM DNS schema'
severity: Medium
requiredDataConnectors:
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: InfobloxNIOS
dataTypes:
- Syslog
- connectorId: GCPDNSDataConnector
dataTypes:
- GCP_DNS_CL
- connectorId: NXLogDnsLogs
dataTypes:
- NXLog_DNS_Server_CL
- connectorId: CiscoUmbrellaDataConnector
dataTypes:
- Cisco_Umbrella_dns_CL
- connectorId: Corelight
dataTypes:
- Corelight_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1568
- T1008
tags:
- ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/InfobloxNIOS/ExcessiveNXDOMAINDNSQueries.yaml
version: 1.0.0
- Schema: ASIMDns
SchemaVersion: 0.1.1
query: |
let threshold = 200;
_Im_Dns(responsecodename='NXDOMAIN')
| where isnotempty(DnsResponseCodeName)
//| where DnsResponseCodeName =~ "NXDOMAIN"
| summarize count() by SrcIpAddr, bin(TimeGenerated,15m)
| where count_ > threshold
| join kind=inner (_Im_Dns(responsecodename='NXDOMAIN')
) on SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
version: 1.3.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Yaron
support:
tier: Community
categories:
domains: [ "Security - Network" ]
Stages and Predicates
Parameters
let threshold = 200;
Stage 1: source
_Im_Dns(responsecodename='NXDOMAIN')
Stage 2: where
| where isnotempty(DnsResponseCodeName)
Stage 3: summarize
| summarize count() by SrcIpAddr, bin(TimeGenerated,15m)
Stage 4: where
| where count_ > threshold
Stage 5: join
| join kind=inner (_Im_Dns(responsecodename='NXDOMAIN')
) on SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DnsResponseCodeName | is_not_null | field:"DnsResponseCodeName" kind:is_not_null | |
count_ | gt |
| field:"count_" kind:gt value:"200" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | summarize |
Exchange workflow MailItemsAccessed operation anomaly
#Identifies anomalous increases in Exchange mail items accessed operations. The query leverages KQL built-in anomaly detection algorithms to find large deviations from baseline patterns. Sudden increases in execution frequency of sensitive actions should be further investigated for malicious activity. Manually change scorethreshold from 1.5 to 3 or higher to reduce the noise based on outliers flagged from the query criteria. Read more about MailItemsAccessed- https://learn.microsoft.com/en-us/purview/audit-log-investigate-accounts
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | MailItemsAccessed |
Rules Detecting the Same Action
These rules filter on the same operation.
Rule Body
id: b4ceb583-4c44-4555-8ecf-39f572e827ba
name: Exchange workflow MailItemsAccessed operation anomaly
description: |
'Identifies anomalous increases in Exchange mail items accessed operations.
The query leverages KQL built-in anomaly detection algorithms to find large deviations from baseline patterns.
Sudden increases in execution frequency of sensitive actions should be further investigated for malicious activity.
Manually change scorethreshold from 1.5 to 3 or higher to reduce the noise based on outliers flagged from the query criteria.
Read more about MailItemsAccessed- https://learn.microsoft.com/en-us/purview/audit-log-investigate-accounts'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity (Exchange)
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
relevantTechniques:
- T1114
tags:
- Solorigate
- NOBELIUM
query: |
let starttime = 14d;
let endtime = 1d;
let timeframe = 1h;
let scorethreshold = 1.5;
let percentthreshold = 50;
// Preparing the time series data aggregated hourly count of MailItemsAccessd Operation in the form of multi-value array to use with time series anomaly function.
let TimeSeriesData =
OfficeActivity
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| where OfficeWorkload=~ "Exchange" and Operation =~ "MailItemsAccessed" and ResultStatus =~ "Succeeded"
| project TimeGenerated, Operation, MailboxOwnerUPN
| make-series Total=count() on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe;
let TimeSeriesAlerts = TimeSeriesData
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, scorethreshold, -1, 'linefit')
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
| where anomalies > 0
| project TimeGenerated, Total, baseline, anomalies, score;
// Joining the flagged outlier from the previous step with the original dataset to present contextual information
// during the anomalyhour to analysts to conduct investigation or informed decisions.
TimeSeriesAlerts | where TimeGenerated > ago(2d)
// Join against base logs since specified timeframe to retrive records associated with the hour of anomoly
| join kind=innerunique (
OfficeActivity
| where TimeGenerated > ago(2d)
| extend DateHour = bin(TimeGenerated, 1h)
| where OfficeWorkload=~ "Exchange" and Operation =~ "MailItemsAccessed" and ResultStatus =~ "Succeeded"
| summarize HourlyCount=count(), TimeGeneratedMax = arg_max(TimeGenerated, *), IPAdressList = make_set(Client_IPAddress, 1000), SourceIPMax= arg_max(Client_IPAddress, *), ClientInfoStringList= make_set(ClientInfoString, 1000) by MailboxOwnerUPN, Logon_Type, TenantId, UserType, TimeGenerated = bin(TimeGenerated, 1h)
| where HourlyCount > 25 // Only considering operations with more than 25 hourly count to reduce False Positivies
| order by HourlyCount desc
) on TimeGenerated
| extend PercentofTotal = round(HourlyCount/Total, 2) * 100
| where PercentofTotal > percentthreshold // Filter Users with count of less than 5 percent of TotalEvents per Hour to remove FPs/ users with very low count of MailItemsAccessed events
| order by PercentofTotal desc
| project-reorder TimeGeneratedMax, Type, OfficeWorkload, Operation, UserId, SourceIPMax, IPAdressList, ClientInfoStringList, HourlyCount, PercentofTotal, Total, baseline, score, anomalies
| extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Client_IPAddress
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIPMax
version: 2.0.6
kind: Scheduled
Stages and Predicates
Parameters
let starttime = 14d;
let endtime = 1d;
let timeframe = 1h;
let scorethreshold = 1.5;
let percentthreshold = 50;
let TimeSeriesData and let TimeSeriesAlerts are inlined into the numbered stages below.
Stages 1 to 9 define let TimeSeriesAlerts as the rule's main pipeline source. Stages 10 to 16 use it.
Stage 1: source
OfficeActivity
Stage 2: where
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
Stage 3: where
| where OfficeWorkload=~ "Exchange" and Operation =~ "MailItemsAccessed" and ResultStatus =~ "Succeeded"
Stage 4: project
| project TimeGenerated, Operation, MailboxOwnerUPN
The stages below score time-series anomalies (make-series, series_decompose_anomalies).
Stage 5: make-series
| make-series Total=count() on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe
Stage 6: extend
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, scorethreshold, -1, 'linefit')
Stage 7: mv-expand
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
Stage 8: where
| where anomalies > 0
Stage 9: project
| project TimeGenerated, Total, baseline, anomalies, score
Stage 10: where
TimeSeriesAlerts
| where TimeGenerated > ago(2d)
Stage 11: join
| join kind=innerunique (
OfficeActivity
| where TimeGenerated > ago(2d)
| extend DateHour = bin(TimeGenerated, 1h)
| where OfficeWorkload=~ "Exchange" and Operation =~ "MailItemsAccessed" and ResultStatus =~ "Succeeded"
| summarize HourlyCount=count(), TimeGeneratedMax = arg_max(TimeGenerated, *), IPAdressList = make_set(Client_IPAddress, 1000), SourceIPMax= arg_max(Client_IPAddress, *), ClientInfoStringList= make_set(ClientInfoString, 1000) by MailboxOwnerUPN, Logon_Type, TenantId, UserType, TimeGenerated = bin(TimeGenerated, 1h)
| where HourlyCount > 25
| order by HourlyCount desc
) on TimeGenerated
Stage 12: extend
extend PercentofTotal
Stage 13: where
where PercentofTotal > 50
Stage 14: sort
sort by PercentofTotal
Stage 15: project-reorder
project-reorder
Stage 16: extend
extend AccountName, AccountUPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
HourlyCount | gt |
| field:"HourlyCount" kind:gt value:"25" |
OfficeWorkload | eq |
| field:"m365::Workload" kind:eq |
Operation | eq |
| field:"Operation" kind:eq value:"MailItemsAccessed" |
PercentofTotal | gt |
| field:"PercentofTotal" kind:gt value:"50" |
ResultStatus | eq |
| field:"ResultStatus" kind:eq value:"Succeeded" |
anomalies | gt |
| field:"anomalies" kind:gt value:"0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TimeGenerated | project |
Total | project |
anomalies | project |
baseline | project |
score | project |
PercentofTotal | extend |
AccountName | extend |
AccountUPNSuffix | extend |
Failover Plan Deleted
#Detects when a failover plan is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.
Rule Body
id: 5f6f5a46-42d3-4961-94db-68b6229bc84e
name: Failover Plan Deleted
description: Detects when a failover plan is deleted from Veeam Backup & Replication.
This might indicate unauthorized removal of critical components.
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: "Veeam_GetSecurityEvents\n| where instanceId == 26100\n| extend FailoverPlanName\
\ = extract(\"FailoverPlanName=\\\"(?<FailoverPlanName>[^\\\"]*)\\\"\", 1, SyslogMessage)\n\
| project\n Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource\
\ = original_host,\n EventId = instanceId,\n UserName = user,\n [\"Failover\
\ Plan Name\"] = FailoverPlanName,\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 == 26100
Stage 3: extend
| extend FailoverPlanName = extract("FailoverPlanName=\"(?<FailoverPlanName>[^\"]*)\"", 1, SyslogMessage)
Stage 4: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
UserName = user,
["Failover Plan Name"] = FailoverPlanName,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"26100" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
Failover Plan Name | project |
MessageDetails | project |
Severity | project |
UserName | project |
GCP Audit Logs - DNSSEC Disabled on Managed DNS Zone
#Detects when DNSSEC (DNS Security Extensions) is disabled on a Google Cloud DNS managed zone. DNSSEC provides cryptographic authentication of DNS data, preventing DNS spoofing and cache poisoning attacks. Adversaries may disable DNSSEC to enable DNS-based command and control, phishing campaigns, or to redirect traffic to malicious infrastructure without cryptographic validation. This rule monitors DNS zone patch operations where DNSSEC state changes from ON to OFF.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Resource Development | |
| Defense Impairment | |
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| GCP | dns.managedZones.patch: Patch managed zone |
| GCP | dns.managedZones.update: Update managed zone |
Rule Body
id: 9129a43e-e204-4a9a-969e-d8861ce3437c
name: GCP Audit Logs - DNSSEC Disabled on Managed DNS Zone
description: |
'Detects when DNSSEC (DNS Security Extensions) is disabled on a Google Cloud DNS managed zone.
DNSSEC provides cryptographic authentication of DNS data, preventing DNS spoofing and cache poisoning attacks.
Adversaries may disable DNSSEC to enable DNS-based command and control, phishing campaigns, or
to redirect traffic to malicious infrastructure without cryptographic validation.
This rule monitors DNS zone patch operations where DNSSEC state changes from ON to OFF.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: GCPAuditLogsDefinition
dataTypes:
- GCPAuditLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
- CommandAndControl
- ResourceDevelopment
relevantTechniques:
- T1562.001
- T1071.004
- T1584.002
tags:
- GCP
- DNS
- DNSSEC
- Cloud Security
query: |
GCPAuditLogs
| where ServiceName == "dns.googleapis.com"
| where MethodName in ("dns.managedZones.update", "dns.managedZones.patch")
| where GCPResourceType == "dns_managed_zone" and Severity == "NOTICE"
| extend
ResponseJson = parse_json(Response),
RequestMetadataJson = parse_json(RequestMetadata),
AuthInfoJson = parse_json(AuthenticationInfo)
| extend ZoneContext = ResponseJson.operation.zoneContext
| where isnotempty(ZoneContext)
| extend
OldDnsSecState = tostring(ZoneContext.oldValue.dnssecConfig.state),
NewDnsSecState = tostring(ZoneContext.newValue.dnssecConfig.state)
| where OldDnsSecState == "ON" and NewDnsSecState == "OFF"
| extend
ManagedZoneName = extract(@"managedZones/([^/]+)", 1, GCPResourceName),
DnsName = tostring(ResponseJson.managedZone.dnsName),
ZoneId = tostring(ResponseJson.managedZone.id),
ZoneDescription = tostring(ResponseJson.managedZone.description),
Visibility = tostring(ResponseJson.managedZone.visibility),
OperationId = tostring(ResponseJson.operation.id),
CallerIpAddress = tostring(RequestMetadataJson.callerIp),
AuthEmail = tostring(AuthInfoJson.principalEmail)
| extend
AccountName = tostring(split(PrincipalEmail, "@")[0]),
AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])
| project TimeGenerated,
PrincipalEmail,
AuthEmail,
ProjectId,
ManagedZoneName,
DnsName,
ResourceName = GCPResourceName,
Visibility,
ZoneId,
ZoneDescription,
OperationId,
CallerIpAddress,
MethodName,
ServiceName,
Severity,
LogName,
InsertId,
AccountName,
AccountUPNSuffix
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: PrincipalEmail
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CallerIpAddress
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: ProjectId
- identifier: InstanceName
columnName: ResourceName
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DnsName
customDetails:
ProjectId: ProjectId
ManagedZoneName: ManagedZoneName
DnsName: DnsName
ResourceName: ResourceName
Visibility: Visibility
ZoneId: ZoneId
alertDetailsOverride:
alertDisplayNameFormat: "DNSSEC Disabled on DNS Zone {{ManagedZoneName}} ({{DnsName}}) by {{PrincipalEmail}}"
alertDescriptionFormat: |-
User {{PrincipalEmail}} disabled DNSSEC on DNS managed zone {{ManagedZoneName}} ({{DnsName}}).
This action removes cryptographic validation of DNS responses and may indicate an attempt to facilitate DNS-based attacks.
Investigate immediately to determine if this change was authorized and assess potential security impact.
Review DNS query logs for suspicious activity and consider re-enabling DNSSEC if unauthorized.
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
GCPAuditLogs
Stage 2: where
| where ServiceName == "dns.googleapis.com"
Stage 3: where
| where MethodName in ("dns.managedZones.update", "dns.managedZones.patch")
Stage 4: where
| where GCPResourceType == "dns_managed_zone" and Severity == "NOTICE"
Stage 5: extend
| extend
ResponseJson = parse_json(Response),
RequestMetadataJson = parse_json(RequestMetadata),
AuthInfoJson = parse_json(AuthenticationInfo)
Stage 6: extend
| extend ZoneContext = ResponseJson.operation.zoneContext
Stage 7: where
| where isnotempty(ZoneContext)
Stage 8: extend
| extend
OldDnsSecState = tostring(ZoneContext.oldValue.dnssecConfig.state),
NewDnsSecState = tostring(ZoneContext.newValue.dnssecConfig.state)
Stage 9: where
| where OldDnsSecState == "ON" and NewDnsSecState == "OFF"
Stage 10: extend
| extend
ManagedZoneName = extract(@"managedZones/([^/]+)", 1, GCPResourceName),
DnsName = tostring(ResponseJson.managedZone.dnsName),
ZoneId = tostring(ResponseJson.managedZone.id),
ZoneDescription = tostring(ResponseJson.managedZone.description),
Visibility = tostring(ResponseJson.managedZone.visibility),
OperationId = tostring(ResponseJson.operation.id),
CallerIpAddress = tostring(RequestMetadataJson.callerIp),
AuthEmail = tostring(AuthInfoJson.principalEmail)
Stage 11: extend
| extend
AccountName = tostring(split(PrincipalEmail, "@")[0]),
AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])
Stage 12: project
| project TimeGenerated,
PrincipalEmail,
AuthEmail,
ProjectId,
ManagedZoneName,
DnsName,
ResourceName = GCPResourceName,
Visibility,
ZoneId,
ZoneDescription,
OperationId,
CallerIpAddress,
MethodName,
ServiceName,
Severity,
LogName,
InsertId,
AccountName,
AccountUPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
GCPResourceType | eq |
| field:"gcp::resource_type" kind:eq value:"dns_managed_zone" |
MethodName | in |
| field:"gcp::method_name" kind:in |
NewDnsSecState | eq |
| field:"NewDnsSecState" kind:eq value:"OFF" |
OldDnsSecState | eq |
| field:"OldDnsSecState" kind:eq value:"ON" |
ServiceName | eq |
| field:"ServiceName" kind:eq value:"dns.googleapis.com" |
Severity | eq |
| field:"Severity" kind:eq value:"NOTICE" |
ZoneContext | is_not_null | field:"ZoneContext" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | project |
AccountUPNSuffix | project |
AuthEmail | project |
CallerIpAddress | project |
DnsName | project |
InsertId | project |
LogName | project |
ManagedZoneName | project |
MethodName | project |
OperationId | project |
PrincipalEmail | project |
ProjectId | project |
ResourceName | project |
ServiceName | project |
Severity | project |
TimeGenerated | project |
Visibility | project |
ZoneDescription | project |
ZoneId | project |
GCP IAM - Privileges Enumeration
#Detects possible privileges enumeration.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| GCP | google.iam.admin.ListRoles: ListRoles |
Rule Body
id: 52d88912-fa8b-4db2-b247-ee9225e41e8f
name: GCP IAM - Privileges Enumeration
description: |
'Detects possible privileges enumeration.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: GCPIAMDataConnector
dataTypes:
- GCP_IAM
queryFrequency: 12h
queryPeriod: 12h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1069
query: |
let threshold = 5;
GCP_IAM
| where PayloadMethodname =~ 'google.iam.admin.v1.ListRoles'
| summarize count() by PayloadAuthenticationinfoPrincipalemail, bin(TimeGenerated, 1h)
| where count_ > threshold
| extend AccountCustomEntity = PayloadAuthenticationinfoPrincipalemail
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 5;
Stage 1: source
GCP_IAM
Stage 2: where
| where PayloadMethodname =~ 'google.iam.admin.v1.ListRoles'
Stage 3: summarize
| summarize count() by PayloadAuthenticationinfoPrincipalemail, bin(TimeGenerated, 1h)
Stage 4: where
| where count_ > threshold
Stage 5: extend
| extend AccountCustomEntity = PayloadAuthenticationinfoPrincipalemail
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PayloadMethodname | eq |
| field:"gcp::method_name" kind:eq value:"google.iam.admin.v1.ListRoles" |
count_ | gt |
| field:"count_" kind:gt value:"5" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
PayloadAuthenticationinfoPrincipalemail | summarize |
AccountCustomEntity | extend |
KnowBe4 Defend - Dangerous Link Click
#'Defend has detected a user has clicked a dangerous link in their mailbox.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Execution |
Rule Body
id: af90173d-cc27-4166-a9b1-8c4347b1f4ac
name: KnowBe4 Defend - Dangerous Link Click
description: |
'Defend has detected a user has clicked a dangerous link in their mailbox.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: KnowBe4Defend
datatypes:
- KnowBe4Defend_CL
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1204
- T0853
tags:
- Defend
query: |
DefendAuditData
| where LinksClicked > 0
| where ThreatLevel == "dangerous" or ThreatLevel == "suspicious"
| extend Account_0_FullName = trim(@"[^@.\w]+",Recipients)
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Account_0_FullName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SenderIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: Mailbox
fieldMappings:
- identifier: MailboxPrimaryAddress
columnName: Account_0_FullName
customDetails:
DefendSenderIP: SenderIP
DefendSender: From
timesClicked: LinksClicked
alertDetailsOverride:
alertDisplayNameFormat: Alert - {{Account_0_FullName}} as clicked a suspicious link.
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
DefendAuditData
Stage 2: where
| where LinksClicked > 0
Stage 3: where
| where ThreatLevel == "dangerous" or ThreatLevel == "suspicious"
Stage 4: extend
| extend Account_0_FullName = trim(@"[^@.\w]+",Recipients)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
LinksClicked | gt |
| field:"LinksClicked" kind:gt value:"0" |
ThreatLevel | eq |
| field:"ThreatLevel" kind:eq |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account_0_FullName | extend |
LSASS Credential Dumping with Procdump
#This query was originally published in the threat analytics report, "Exchange Server zero-days exploited in the wild". In early March 2021, Microsoft released patches for four different zero-day vulnerabilities affecting Microsoft Exchange Server. The vulnerabilities were being used in a coordinated attack. For more information on the vulnerabilities, visit the following links: 1. CVE-2021-26855 2. CVE-2021-26857 3. CVE-2021-26858 4. CVE-2021-27065 The following query looks for evidence of Procdump being used to dump credentials from LSASS, the Local Security Authentication Server. This might indicate an attacker has compromised user accounts. More queries related to this threat can be found under the See also section of this page. Reference - https://msrc-blog.microsoft.com/2021/03/02/multiple-security-updates-released-for-exchange-server/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action any: Process activity |
Rule Body
id: c332b840-61e4-462e-a201-0e2d69bad45d
name: LSASS Credential Dumping with Procdump
description: |
This query was originally published in the threat analytics report, "Exchange Server zero-days exploited in the wild".
In early March 2021, Microsoft released patches for four different zero-day vulnerabilities affecting Microsoft Exchange Server. The vulnerabilities were being used in a coordinated attack. For more information on the vulnerabilities, visit the following links:
1. CVE-2021-26855
2. CVE-2021-26857
3. CVE-2021-26858
4. CVE-2021-27065
The following query looks for evidence of Procdump being used to dump credentials from LSASS, the Local Security Authentication Server. This might indicate an attacker has compromised user accounts.
More queries related to this threat can be found under the See also section of this page.
Reference - https://msrc-blog.microsoft.com/2021/03/02/multiple-security-updates-released-for-exchange-server/
severity: High
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1003
tags:
- Exchange Server
- Zero-day
- Procdump
- Credential Dumping
- LSASS
query: |
DeviceProcessEvents
| where (FileName has_any ("procdump.exe", "procdump64.exe") and ProcessCommandLine has "lsass") or
// Looking for Accepteula flag or Write a dump file with all process memory
(ProcessCommandLine has "lsass.exe" and (ProcessCommandLine has "-accepteula" or ProcessCommandLine contains "-ma"))
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
DeviceProcessEvents
Stage 2: where
| where (FileName has_any ("procdump.exe", "procdump64.exe") and ProcessCommandLine has "lsass") or
(ProcessCommandLine has "lsass.exe" and (ProcessCommandLine has "-accepteula" or ProcessCommandLine contains "-ma"))
Stage 3: extend
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =DeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))DeviceNameStage 4: extend
| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =DeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))""Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileName | match |
| field:"process_basename" kind:match |
ProcessCommandLine | contains |
| field:"CommandLine" kind:contains value:"-ma" |
ProcessCommandLine | match |
| field:"CommandLine" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostName | extend |
DnsDomain | extend |
Malware Link Clicked
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
'This query identifies a user clicking on an email link whose threat category is classified as a malware'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 8675dd7a-795e-4d56-a79c-fc848c5ee61c
name: Malware Link Clicked
description: |
'This query identifies a user clicking on an email link whose threat category is classified as a malware'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ProofpointTAPv2
dataTypes:
- ProofPointTAPClicksPermittedV2_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566.002
query: |
ProofPointTAPClicksPermittedV2_CL
| where classification =~ "malware"
| where threatStatus != "cleared"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by TimeGenerated, Sender = sender, SenderIPAddress = senderIP, Recipient = recipient, TimeClicked = clickTime, URLClicked = url
| extend RecipientName = tostring(split(Recipient, "@")[0]), RecipientUPNSuffix = tostring(split(Recipient, "@")[1])
| extend SenderName = tostring(split(Sender, "@")[0]), SenderUPNSuffix = tostring(split(Sender, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Recipient
- identifier: Name
columnName: RecipientName
- identifier: UPNSuffix
columnName: RecipientUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Sender
- identifier: Name
columnName: SenderName
- identifier: UPNSuffix
columnName: SenderUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SenderIPAddress
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URLClicked
version: 1.0.7
kind: Scheduled
Stages and Predicates
Stage 1: source
ProofPointTAPClicksPermittedV2_CL
Stage 2: where
| where classification =~ "malware"
Stage 3: where
| where threatStatus != "cleared"
Stage 4: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by TimeGenerated, Sender = sender, SenderIPAddress = senderIP, Recipient = recipient, TimeClicked = clickTime, URLClicked = url
Stage 5: extend
| extend RecipientName = tostring(split(Recipient, "@")[0]), RecipientUPNSuffix = tostring(split(Recipient, "@")[1])
Stage 6: extend
| extend SenderName = tostring(split(Sender, "@")[0]), SenderUPNSuffix = tostring(split(Sender, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
classification | eq |
| field:"classification" kind:eq value:"malware" |
threatStatus | ne |
| field:"threatStatus" kind:ne value:"cleared" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EndTime | summarize |
Recipient | summarize |
Sender | summarize |
SenderIPAddress | summarize |
StartTime | summarize |
TimeClicked | summarize |
TimeGenerated | summarize |
URLClicked | summarize |
RecipientName | extend |
RecipientUPNSuffix | extend |
SenderName | extend |
SenderUPNSuffix | extend |
Mimecast Data Leak Prevention - Notifications
#Detects threat for data leak when action is notification
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: cfd67598-ad0d-430a-a793-027eb4dbe967
name: Mimecast Data Leak Prevention - Notifications
description: Detects threat for data leak when action is notification
severity: High
requiredDataConnectors:
- connectorId: MimecastSEGAPI
dataTypes:
- MimecastDLP
enabled: true
query: |
MimecastDLP
| where Action == "notification"
| extend SenderAddress = ['Sender Address'] ,RecipientAddress = ['Recipient Address']
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Exfiltration
relevantTechniques:
- T1030
alertRuleTemplateName:
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P7D
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: SenderAddress
- identifier: Recipient
columnName: RecipientAddress
- identifier: DeliveryAction
columnName: Action
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastDLP
Stage 2: where
| where Action == "notification"
Stage 3: extend
| extend SenderAddress = ['Sender Address'] ,RecipientAddress = ['Recipient Address']
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | eq |
| field:"Action" kind:eq value:"notification" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
RecipientAddress | extend |
SenderAddress | extend |
Pathlock TDnR - Business Partner Bank Data Changes
#Detects changes to bank master data for business partners in SAP, forwarded by Pathlock Threat Detection and Response. Modifications to business partner bank accounts may indicate fraud targeting vendor or customer payments.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c06
name: Pathlock TDnR - Business Partner Bank Data Changes
kind: Scheduled
description: >-
Detects changes to bank master data for business partners in SAP, forwarded by Pathlock Threat Detection
and Response. Modifications to business partner bank accounts may indicate fraud targeting
vendor or customer payments.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1565
query: |
Pathlock_TDnR_CL
| where DataSource == "CHANGEDOC_BUPA_BANK"
| 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_BUPA_BANK"
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_BUPA_BANK" |
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 - Password reset request from unexpected source IP address..
#'Detects password reset requests from unexpected source IP address.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 2d201d21-77b4-4d97-95f3-26b5c6bde09f
name: Ping Federate - Password reset request from unexpected source IP address..
description: |
'Detects password reset requests from unexpected source IP address.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
let known_src =
PingFederateEvent
| where TimeGenerated between (ago(14d) .. (1d))
| where isnotempty(SrcIpAddr)
| summarize makeset(SrcIpAddr);
PingFederateEvent
| where EventType =~ 'PWD_RESET_REQUEST'
| where isnotempty(SrcIpAddr)
| where SrcIpAddr !in (known_src)
| 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_src
let known_src = PingFederateEvent
| where TimeGenerated between (ago(14d) .. (1d))
| where isnotempty(SrcIpAddr)
| summarize makeset(SrcIpAddr);
Stage 1: source
PingFederateEvent
Stage 2: where
| where EventType =~ 'PWD_RESET_REQUEST'
Stage 3: where
| where isnotempty(SrcIpAddr)
Stage 4: where
| where SrcIpAddr !in (known_src)
Stage 5: extend
| extend AccountCustomEntity = DstUserName
Stage 6: extend
| extend IpCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"PWD_RESET_REQUEST" |
SrcIpAddr | is_not_null | field:"src_ip" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | eq | known_src | excludes:SrcIpAddr field:"SrcIpAddr" value:"known_src" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IpCustomEntity | extend |
Power Apps - Multiple apps deleted
#Identifies mass delete activity where multiple Power Apps are deleted, matching a predefined threshold of total apps deleted or app delete events across multiple Power Platform environments.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact | |
| Impact |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | any: Power Platform administrator activity (catch-all) |
Rule Body
id: ed88638d-8627-4c20-ba08-67c13807a9b1
kind: Scheduled
name: Power Apps - Multiple apps deleted
description: Identifies mass delete activity where multiple Power Apps are deleted,
matching a predefined threshold of total apps deleted or app delete events across
multiple Power Platform environments.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: PowerPlatformAdmin
dataTypes:
- PowerPlatformAdminActivity
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
- T0826
query: |
let total_app_mass_delete_threshold = 25;
let cross_environment_delete_threshold = 10;
let query_frequency = 1h;
let app_delete_events = materialize(
PowerPlatformAdminActivity
| where TimeGenerated >= ago (query_frequency)
| where EventOriginalType == "DeletePowerApp"
| extend Properties = tostring(PropertyCollection)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties)
);
app_delete_events
| summarize AppCount = count(), EnvCount = dcount(EnvironmentId) by ActorName
| where AppCount >= total_app_mass_delete_threshold or EnvCount >= cross_environment_delete_threshold
| join kind=inner app_delete_events on ActorName
| summarize
Apps = make_set(AppId, 1000),
Environments = make_set(EnvironmentId, 1000),
StartTime = min(TimeGenerated)
by AppCount, EnvCount, ActorName
| extend
PowerAppsEntityId = 27593,
DataverseId = 32780,
AccountName = tostring(split(ActorName, '@')[0]),
UPNSuffix = tostring(split(ActorName, '@')[1])
| project
StartTime,
ActorName,
AppCount,
Apps,
EnvCount,
Environments,
PowerAppsEntityId,
DataverseId,
AccountName,
UPNSuffix
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: PowerAppsEntityId
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
alertDetailsOverride:
alertDisplayNameFormat: Power Apps - mass deletion of apps
alertDescriptionFormat: '{{AppCount}} apps were deleted in {{EnvCount}} environments
by {{ActorName}} , exceeding the mass delete threshold.'
customDetails:
EnvironmentsImpacted: Environments
AppsDeleted: Apps
AppDeleteCount: AppCount
EnvironmentsCount: EnvCount
version: 3.2.0
Stages and Predicates
Parameters
let total_app_mass_delete_threshold = 25;
let cross_environment_delete_threshold = 10;
let query_frequency = 1h;
let app_delete_events is inlined into the numbered stages below.
Stages 1 to 4 define let app_delete_events as the rule's main pipeline source. Stages 5 to 10 use it.
Stage 1: source
PowerPlatformAdminActivity
Stage 2: where
| where TimeGenerated >= ago (query_frequency)
Stage 3: where
| where EventOriginalType == "DeletePowerApp"
Stage 4: extend (4 consecutive steps)
| extend Properties = tostring(PropertyCollection)
| extend AppId = extract(@'"powerplatform.analytics.resource.power_app.id","Value":"([^"]+)"', 1, Properties)
| extend AppId = tolower(replace_string(AppId, '/providers/Microsoft.PowerApps/apps/', ''))
| extend EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties)
Stage 5: summarize
app_delete_events
| summarize AppCount = count(), EnvCount = dcount(EnvironmentId) by ActorName
Stage 6: where
| where AppCount >= total_app_mass_delete_threshold or EnvCount >= cross_environment_delete_threshold
Stage 7: join
| join kind=inner app_delete_events on ActorName
Stage 8: summarize
summarize Apps, Environments, StartTime by AppCount, EnvCount, ActorName
Stage 9: extend
extend AccountName, DataverseId, PowerAppsEntityId, UPNSuffix
Stage 10: project
project AccountName, ActorName, AppCount, Apps, DataverseId, EnvCount, Environments, PowerAppsEntityId, StartTime, UPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AppCount | ge |
| field:"AppCount" kind:ge value:"25" |
EnvCount | ge |
| field:"EnvCount" kind:ge value:"10" |
EventOriginalType | eq |
| field:"EventOriginalType" kind:eq value:"DeletePowerApp" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | project |
ActorName | project |
AppCount | project |
Apps | project |
DataverseId | project |
EnvCount | project |
Environments | project |
PowerAppsEntityId | project |
StartTime | project |
UPNSuffix | project |
Semperis DSP Well-known privileged SIDs in sIDHistory
#'This indicator looks for security principals that contain specific SIDs of accounts from built-in privileged groups within their sIDHistory attribute. This would allow those security principals to have the same privileges as those privileged accounts, but in a way that is not obvious to monitor (e.g. through group membership).'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | |
| Stealth |
Rule Body
id: ddd75d93-5b8b-4349-babe-c4e15343c5a3
name: Semperis DSP Well-known privileged SIDs in sIDHistory
description: |
'This indicator looks for security principals that contain specific SIDs of accounts from built-in privileged groups within their sIDHistory attribute. This would allow those security principals to have the same privileges as those privileged accounts, but in a way that is not obvious to monitor (e.g. through group membership).'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SemperisDSP
dataTypes:
- dsp_parser
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- DefenseEvasion
relevantTechniques:
- T1134
query: |
dsp_parser
| where EventID == 9212
| where SecurityIndicatorName == "Well-known privileged SIDs in sIDHistory"
| 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 == "Well-known privileged SIDs in sIDHistory"
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:"Well-known privileged SIDs in sIDHistory" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
SlackAudit - Empty User Agent
#'This query shows connections to the Slack Workspace with empty User Agent.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 04528635-a5f1-438b-ab74-21ca7bc3aa32
name: SlackAudit - Empty User Agent
description: |
'This query shows connections to the Slack Workspace with empty User Agent.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: SlackAuditAPI
dataTypes:
- SlackAudit_CL
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1133
query: |
SlackAudit
| where isempty(UserAgentOriginal)
| extend AccountCustomEntity = SrcUserName
| project TimeGenerated, AccountCustomEntity, SrcUserName, UserAgentOriginal
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
alertDetailsOverride:
alertDisplayNameFormat: Slack audit empty user agent for {{SrcUserName}}
alertDescriptionFormat: Slack activity from {{SrcUserName}} at {{TimeGenerated}}.
customDetails:
SlackUser: SrcUserName
UserAgent: UserAgentOriginal
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
SlackAudit
Stage 2: where
| where isempty(UserAgentOriginal)
Stage 3: extend
| extend AccountCustomEntity = SrcUserName
Stage 4: project
| project TimeGenerated, AccountCustomEntity, SrcUserName, UserAgentOriginal
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
UserAgentOriginal | is_null | field:"UserAgentOriginal" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | project |
SrcUserName | project |
TimeGenerated | project |
UserAgentOriginal | project |
SonicWall - Allowed SSH, Telnet, and RDP Connections
#'This rule identifies allowed inbound SSH, Telnet, and RDP connections. This analytic rule leverages the SonicWall Firewall ASIM Network Session parser (ASimNetworkSessionSonicWallFirewall).'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Credential Access | |
| Discovery | |
| Lateral Movement | |
| Collection | |
| Exfiltration | |
| Impact |
Rule Body
id: 27f1a570-5f20-496b-88f6-a9aa2c5c9534
name: SonicWall - Allowed SSH, Telnet, and RDP Connections
description: |
'This rule identifies allowed inbound SSH, Telnet, and RDP connections. This analytic rule leverages the SonicWall Firewall ASIM Network Session parser (ASimNetworkSessionSonicWallFirewall).'
severity: Medium
status: Experimental
requiredDataConnectors:
- connectorId: CEF
dataTypes:
- CommonSecurityLog
- connectorId: SonicWallFirewall
dataTypes:
- ASimNetworkSessionSonicWallFirewall
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
- Persistence
- CredentialAccess
- Discovery
- LateralMovement
- Collection
- Exfiltration
- Impact
relevantTechniques:
- T1190
- T1133
- T1059
- T1133
- T1110
- T1003
- T1087
- T1018
- T1021
- T1005
- T1048
- T1041
- T1011
- T1567
- T1490
query: |
ASimNetworkSessionSonicWallFirewall(false)
| where NetworkDirection == "Inbound" and EventResult in ("NA", "Success")
| where EventOriginalType != 1370
| where SrcAppName contains "Windows Remote Desktop Services"
or SrcAppName contains "Telnet"
or (SrcAppName contains "SSH Protocol" and ThreatId != 446) // Filters out SSH server responses.
or SrcAppName contains "Bitvise SSH"
or DstPortNumber in (22, 23, 3389)
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DstIpAddr
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
ASimNetworkSessionSonicWallFirewall(false)
Stage 2: where
| where NetworkDirection == "Inbound" and EventResult in ("NA", "Success")
Stage 3: where
| where EventOriginalType != 1370
Stage 4: where
| where SrcAppName contains "Windows Remote Desktop Services"
or SrcAppName contains "Telnet"
or (SrcAppName contains "SSH Protocol" and ThreatId != 446)
or SrcAppName contains "Bitvise SSH"
or DstPortNumber in (22, 23, 3389)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DstPortNumber | in |
| field:"DstPortNumber" kind:in |
EventOriginalType | ne |
| field:"EventOriginalType" kind:ne value:"1370" |
EventResult | in |
| field:"EventResult" kind:in |
NetworkDirection | eq |
| field:"NetworkDirection" kind:eq value:"Inbound" |
SrcAppName | contains |
| field:"SrcAppName" kind:contains |
ThreatId | ne |
| field:"ThreatId" kind:ne value:"446" |
Subtenant Updated
#Detects when subtenant settings are updated in Veeam Backup & Replication.
Rule Body
id: e1d0d9ce-b7c9-4e28-883d-04429f80d6d7
name: Subtenant Updated
description: Detects when subtenant settings are updated in Veeam Backup & Replication.
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 == 25220\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n [\"User Name\"] = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 25220
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
["User Name"] = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"25220" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
User Name | project |
TI Map IP Entity to DnsEvents
#This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DnsEvents.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 69b7723c-2889-469f-8b55-a2d355ed9c87
name: TI Map IP Entity to DnsEvents
description: |
This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DnsEvents.
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h; // Look back 1 hour for DNS events
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = ThreatIntelligenceIndicator
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| where TimeGenerated >= ago(ioc_lookBack)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now();
// Perform a join between IP indicators and DNS events
IP_Indicators
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (
DnsEvents
| where TimeGenerated >= ago(dt_lookBack)
| where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
| mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
| extend DNS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SingleIP
// Filter out DNS events that occurred after the expiration of the corresponding indicator
| where DNS_TimeGenerated < ExpirationDateTime
// Group the results by IndicatorId and SingleIP, and keep the DNS event with the latest timestamp
| summarize DNS_TimeGenerated = arg_max(DNS_TimeGenerated, *) by IndicatorId, SingleIP
// Select the desired output fields
| project DNS_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, DomainName, ExpirationDateTime, ConfidenceScore,
TI_ipEntity, Computer, EventId, SubType, ClientIP, Name, IPAddresses, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, Type
| extend timestamp = DNS_TimeGenerated, HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.4.3
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 7 define let IP_Indicators as the rule's main pipeline source. Stages 8 to 12 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 3: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 4: extend (3 consecutive steps)
| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =isnotempty(NetworkIP)NetworkIPNetworkDestinationIPStage 5: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 6: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 7: where
| where Active == true and ExpirationDateTime > now()
Stage 8: join
IP_Indicators
| join kind=innerunique (
DnsEvents
| where TimeGenerated >= ago(dt_lookBack)
| where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
| mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
| extend DNS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SingleIP
Stage 9: where
where DNS_TimeGenerated < ExpirationDateTime
Stage 10: summarize
summarize DNS_TimeGenerated by IndicatorId, SingleIP
Stage 11: project
project ActivityGroupNames, ClientIP, Computer, ConfidenceScore, DNS_TimeGenerated, Description, DomainName, EmailSourceIpAddress, EventId, ExpirationDateTime, IPAddresses, IndicatorId, Name, NetworkDestinationIP, NetworkIP, NetworkSourceIP, SubType, TI_ipEntity, ThreatType, Type, Url
Stage 12: extend
extend DnsDomain, HostName, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
EmailSourceIpAddress | is_not_null | field:"EmailSourceIpAddress" kind:is_not_null | |
IPAddresses | is_not_null | field:"IPAddresses" kind:is_not_null | |
NetworkDestinationIP | is_not_null | field:"NetworkDestinationIP" kind:is_not_null | |
NetworkIP | is_not_null | field:"NetworkIP" kind:is_not_null | |
NetworkSourceIP | is_not_null | field:"NetworkSourceIP" kind:is_not_null | |
SubType | eq |
| field:"SubType" kind:eq value:"LookupQuery" |
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 |
ClientIP | project |
Computer | project |
ConfidenceScore | project |
DNS_TimeGenerated | project |
Description | project |
DomainName | project |
EmailSourceIpAddress | project |
EventId | project |
ExpirationDateTime | project |
IPAddresses | project |
IndicatorId | project |
Name | project |
NetworkDestinationIP | project |
NetworkIP | project |
NetworkSourceIP | project |
SubType | project |
TI_ipEntity | project |
ThreatType | project |
Type | project |
Url | project |
DnsDomain | extend |
HostName | extend |
timestamp | extend |
TI map IP entity to OfficeActivity
#This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in OfficeActivity.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: f15370f4-c6fa-42c5-9be4-1d308f40284e
name: TI map IP entity to OfficeActivity
description: |
This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in OfficeActivity.
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: Office365
dataTypes:
- OfficeActivity
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h; // Look back 1 hour for OfficeActivity events
let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
let OfficeActivity_ = materialize(OfficeActivity
| where isnotempty(ClientIP)
| where TimeGenerated >= ago(dt_lookBack)
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]%]+)(%\d+)?\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend IPAddress = iff(array_length(ClientIPValues) > 0, tostring(ClientIPValues[0]), '')
| project-rename OfficeActivity_TimeGenerated = TimeGenerated);
let ActivityIPs = OfficeActivity_ | summarize IPs = make_list(IPAddress);
// Fetch threat intelligence indicators related to IP addresses
let IP_Indicators = materialize(ThreatIntelligenceIndicator
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| where TimeGenerated >= ago(ioc_lookBack)
| extend TI_ipEntity = coalesce(NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress)
| where TI_ipEntity in (ActivityIPs)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;");
IP_Indicators
// Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
| join kind=innerunique (OfficeActivity_)
on $left.TI_ipEntity == $right.IPAddress
// Filter out OfficeActivity events that occurred after the expiration of the corresponding indicator
| where OfficeActivity_TimeGenerated < ExpirationDateTime
// Group the results by IndicatorId and keep the OfficeActivity event with the latest timestamp
| summarize OfficeActivity_TimeGenerated = arg_max(OfficeActivity_TimeGenerated, *) by IndicatorId
// Select the desired output fields
| project OfficeActivity_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, TI_ipEntity, ClientIP, UserId, Operation, ResultStatus, RecordType, OfficeObjectId, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, Type
| extend timestamp = OfficeActivity_TimeGenerated, Name = tostring(split(UserId, '@', 0)[0]), UPNSuffix = tostring(split(UserId, '@', 1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: TI_ipEntity
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
version: 1.4.3
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Let Binding: OfficeActivity_
let OfficeActivity_ = materialize(OfficeActivity
| where isnotempty(ClientIP)
| where TimeGenerated >= ago(dt_lookBack)
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]%]+)(%\d+)?\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend IPAddress = iff(array_length(ClientIPValues) > 0, tostring(ClientIPValues[0]), '')
| project-rename OfficeActivity_TimeGenerated = TimeGenerated);
Let Binding: ActivityIPs
let ActivityIPs = OfficeActivity_ | summarize IPs = make_list(IPAddress);
Stages 1 to 8 define let IP_Indicators as the rule's main pipeline source. Stages 9 to 13 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 3: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 4: extend
| extend TI_ipEntity = coalesce(NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress)
Stage 5: where
| where TI_ipEntity in (ActivityIPs)
Stage 6: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 7: where
| where Active == true and ExpirationDateTime > now()
Stage 8: where
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
Stage 9: join
IP_Indicators
| join kind=innerunique (OfficeActivity_)
on $left.TI_ipEntity == $right.IPAddress
Stage 10: where
where OfficeActivity_TimeGenerated < ExpirationDateTime
Stage 11: summarize
summarize OfficeActivity_TimeGenerated by IndicatorId
Stage 12: project
project ActivityGroupNames, ClientIP, ConfidenceScore, Description, EmailSourceIpAddress, ExpirationDateTime, IndicatorId, NetworkDestinationIP, NetworkIP, NetworkSourceIP, OfficeActivity_TimeGenerated, OfficeObjectId, Operation, RecordType, ResultStatus, TI_ipEntity, ThreatType, Type, Url, UserId
Stage 13: extend
extend Name, UPNSuffix, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
ClientIP | is_not_null | field:"ClientIP" kind:is_not_null | |
EmailSourceIpAddress | is_not_null | field:"EmailSourceIpAddress" kind:is_not_null | |
NetworkDestinationIP | is_not_null | field:"NetworkDestinationIP" kind:is_not_null | |
NetworkIP | is_not_null | field:"NetworkIP" kind:is_not_null | |
NetworkSourceIP | is_not_null | field:"NetworkSourceIP" kind:is_not_null | |
TI_ipEntity | in |
| field:"TI_ipEntity" kind:in value:"ActivityIPs" |
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 |
|---|---|
ActivityGroupNames | project |
ClientIP | project |
ConfidenceScore | project |
Description | project |
EmailSourceIpAddress | project |
ExpirationDateTime | project |
IndicatorId | project |
NetworkDestinationIP | project |
NetworkIP | project |
NetworkSourceIP | project |
OfficeActivity_TimeGenerated | project |
OfficeObjectId | project |
Operation | project |
RecordType | project |
ResultStatus | project |
TI_ipEntity | project |
ThreatType | project |
Type | project |
Url | project |
UserId | project |
Name | extend |
UPNSuffix | extend |
timestamp | extend |