Detection rules › Kusto
Kusto rules: 33
Cisco WSA - Multiple errors to resource from risky category
#'Detects multiple connection errors to resource from risky category.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Command & Control |
Rule Body
id: ebf9db0c-ba7b-4249-b9ec-50a05fa7c7c9
name: Cisco WSA - Multiple errors to resource from risky category
description: |
'Detects multiple connection errors to resource from risky category.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CommandAndControl
relevantTechniques:
- T1189
- T1102
query: |
let threshold = 10;
let risky_sites = dynamic(['IW_adlt', 'IW_hack', 'IW_porn']);
CiscoWSAEvent
| where DvcAction startswith 'BLOCK_'
| where UrlCategory in~ (risky_sites)
| summarize count() by SrcUserName, UrlOriginal, bin(TimeGenerated, 5m)
| where count_ >= threshold
| extend URLCustomEntity = UrlOriginal, AccountCustomEntity = SrcUserName
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URLCustomEntity
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 10;
let risky_sites = dynamic(['IW_adlt', 'IW_hack', 'IW_porn']);
Stage 1: source
CiscoWSAEvent
Stage 2: where
| where DvcAction startswith 'BLOCK_'
Stage 3: where
| where UrlCategory in~ (risky_sites)
Stage 4: summarize
| summarize count() by SrcUserName, UrlOriginal, bin(TimeGenerated, 5m)
Stage 5: where
| where count_ >= threshold
Stage 6: extend
| extend URLCustomEntity = UrlOriginal, AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DvcAction | starts_with |
| field:"DvcAction" kind:starts_with value:"BLOCK_" |
UrlCategory | in |
| field:"UrlCategory" kind:in |
count_ | ge |
| field:"count_" kind:ge value:"10" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcUserName | summarize |
UrlOriginal | summarize |
AccountCustomEntity | extend |
URLCustomEntity | extend |
Clearing of forensic evidence from event logs using wevtutil
#This query checks for attempts to clear at least 10 log entries from event logs using wevtutil. Clearing event logs can be a sign of ransomware activity, as ransomware often attempts to cover its tracks by deleting logs.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action any: Process activity |
Rule Body
id: 515d0bba-b297-4f83-8280-20ff7f27ecb1
name: Clearing of forensic evidence from event logs using wevtutil
description: |
This query checks for attempts to clear at least 10 log entries from event logs using wevtutil. Clearing event logs can be a sign of ransomware activity, as ransomware often attempts to cover its tracks by deleting logs.
severity: High
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1070
query: |
// Look for use of wevtutil to clear multiple logs
DeviceProcessEvents
| where ProcessCommandLine has "WEVTUTIL" and ProcessCommandLine has "CL"
| summarize LogClearCount = dcount(tostring(ProcessCommandLine)), ClearedLogList = make_set(ProcessCommandLine, 100000) by DeviceId, DeviceName, bin(TimeGenerated, 5m)
| where LogClearCount > 10
| 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 ProcessCommandLine has "WEVTUTIL" and ProcessCommandLine has "CL"
Stage 3: summarize
| summarize LogClearCount = dcount(tostring(ProcessCommandLine)), ClearedLogList = make_set(ProcessCommandLine, 100000) by DeviceId, DeviceName, bin(TimeGenerated, 5m)
Stage 4: where
| where LogClearCount > 10
Stage 5: extend
| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =DeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))DeviceNameStage 6: 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 |
|---|---|---|---|
LogClearCount | gt |
| field:"LogClearCount" kind:gt value:"10" |
ProcessCommandLine | match |
| field:"CommandLine" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClearedLogList | summarize |
DeviceId | summarize |
DeviceName | summarize |
LogClearCount | summarize |
HostName | extend |
DnsDomain | extend |
Cognni Incidents for Highly Sensitive Governance Information
#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.
'Display incidents in which highly sensitive governance information was placed at risk by user sharing.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 2926ce29-08d2-4654-b2e8-7d8df70095d9
name: Cognni Incidents for Highly Sensitive Governance Information
description: |
'Display incidents in which highly sensitive governance information was placed at risk by user sharing.'
severity: High
requiredDataConnectors:
- connectorId: CognniSentinelDataConnector
dataTypes:
- CognniIncidents_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- Collection
relevantTechniques:
- T1530
query: |
let highRisk = 3;
let governance = 'Governance Information';
CognniIncidents_CL
| where Severity == highRisk
| where informationType_s == governance
| where TimeGenerated >= ago(5h)
| extend AccountCustomEntity = userId_s
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let highRisk = 3;
let governance = 'Governance Information';
Stage 1: source
CognniIncidents_CL
Stage 2: where
| where Severity == highRisk
Stage 3: where
| where informationType_s == governance
Stage 4: where
| where TimeGenerated >= ago(5h)
Stage 5: extend
| extend AccountCustomEntity = userId_s
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | eq |
| field:"Severity" kind:eq value:"3" |
informationType_s | eq |
| field:"informationType_s" kind:eq value:"Governance Information" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
Cognni Incidents for Medium Sensitivity Legal Information
#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.
'Display incidents in which medium sensitivity legal information was placed at risk by user sharing.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: db750607-d48f-4aef-b238-085f4a9882f1
name: Cognni Incidents for Medium Sensitivity Legal Information
description: |
'Display incidents in which medium sensitivity legal information was placed at risk by user sharing.'
severity: Medium
requiredDataConnectors:
- connectorId: CognniSentinelDataConnector
dataTypes:
- CognniIncidents_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- Collection
relevantTechniques:
- T1530
query: |
let mediumRisk = 2;
let legal = 'Legal Information';
CognniIncidents_CL
| where Severity == mediumRisk
| where informationType_s == legal
| where TimeGenerated >= ago(5h)
| extend AccountCustomEntity = userId_s
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let mediumRisk = 2;
let legal = 'Legal Information';
Stage 1: source
CognniIncidents_CL
Stage 2: where
| where Severity == mediumRisk
Stage 3: where
| where informationType_s == legal
Stage 4: where
| where TimeGenerated >= ago(5h)
Stage 5: extend
| extend AccountCustomEntity = userId_s
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | eq |
| field:"Severity" kind:eq value:"2" |
informationType_s | eq |
| field:"informationType_s" kind:eq value:"Legal Information" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
CTE - Risky Device (Multiple IOC URL Hits)
#A single device has repeatedly hit known-malicious (threat-intel-matched) URLs, indicating persistent compromise risk or repeated user exposure.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Command & Control |
Rule Body
id: e1c0f32d-a57b-42ae-8038-b869bca54fef
name: CTE - Risky Device (Multiple IOC URL Hits)
description: |
A single device has repeatedly hit known-malicious (threat-intel-matched) URLs, indicating persistent compromise risk or repeated user exposure.
severity: Medium
requiredDataConnectors:
- connectorId: cybereinforce_cte
dataTypes:
- CybereinforceCTE_CL
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CommandAndControl
relevantTechniques:
- T1189
- T1071
query: |
CybereinforceCTE_CL
| where EventType == "block.url"
| extend d = parse_json(Details)
| extend
DeviceId = tostring(d.deviceId),
HostName = iff(isnotempty(tostring(d.hostname)), tostring(d.hostname), strcat("cte-device-", substring(EntityId, 0, 8))),
DeviceUser = tostring(d.deviceUser),
DeviceOs = tostring(d.deviceOs),
DeviceVersion = tostring(d.deviceVersion),
RuleId = tostring(d.ruleId),
RuleName = iff(isnotempty(tostring(d.ruleName)), tostring(d.ruleName), "Pattern Match"),
Pattern = tostring(d.pattern),
Url = tostring(d.url),
Action = tostring(d.action),
IsIoc = tobool(d.ioc),
ClientIp = tostring(split(tostring(d.clientIp), ":")[0]),
ClientPort = tostring(split(tostring(d.clientIp), ":")[1]),
UserAgent = tostring(d.userAgent)
| where RuleName == "Cybereinforce Threat Intelligence" or IsIoc == true
| summarize IOC_Hits=count(), BlockedUrls=make_set(Url, 50), ClientIps=make_set(ClientIp, 20), LastSeen=max(TimeGenerated) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId
| where IOC_Hits >= 2
| extend Url = tostring(BlockedUrls[0]), ClientIp = tostring(ClientIps[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIp
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CybereinforceCTE_CL
Stage 2: where
| where EventType == "block.url"
Stage 3: extend
| extend d = parse_json(Details)
Stage 4: extend
| extend
DeviceId = tostring(d.deviceId),
HostName = iff(isnotempty(tostring(d.hostname)), tostring(d.hostname), strcat("cte-device-", substring(EntityId, 0, 8))),
DeviceUser = tostring(d.deviceUser),
DeviceOs = tostring(d.deviceOs),
DeviceVersion = tostring(d.deviceVersion),
RuleId = tostring(d.ruleId),
RuleName = iff(isnotempty(tostring(d.ruleName)), tostring(d.ruleName), "Pattern Match"),
Pattern = tostring(d.pattern),
Url = tostring(d.url),
Action = tostring(d.action),
IsIoc = tobool(d.ioc),
ClientIp = tostring(split(tostring(d.clientIp), ":")[0]),
ClientPort = tostring(split(tostring(d.clientIp), ":")[1]),
UserAgent = tostring(d.userAgent)
HostName =isnotempty(tostring(d.hostname))tostring(d.hostname)strcat("cte-device-", substring(EntityId, 0, 8))RuleName =isnotempty(tostring(d.ruleName))tostring(d.ruleName)"Pattern Match"Stage 5: where
| where RuleName == "Cybereinforce Threat Intelligence" or IsIoc == true
Stage 6: summarize
| summarize IOC_Hits=count(), BlockedUrls=make_set(Url, 50), ClientIps=make_set(ClientIp, 20), LastSeen=max(TimeGenerated) by DeviceId, HostName, DeviceUser, DeviceOs, TenantId
Stage 7: where
| where IOC_Hits >= 2
Stage 8: extend
| extend Url = tostring(BlockedUrls[0]), ClientIp = tostring(ClientIps[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"block.url" |
IOC_Hits | ge |
| field:"IOC_Hits" kind:ge value:"2" |
IsIoc | eq |
| field:"IsIoc" kind:eq value:"true" |
RuleName | eq |
| field:"RuleName" kind:eq value:"Cybereinforce Threat Intelligence" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
BlockedUrls | summarize |
ClientIps | summarize |
DeviceId | summarize |
DeviceOs | summarize |
DeviceUser | summarize |
HostName | summarize |
IOC_Hits | summarize |
LastSeen | summarize |
TenantId | summarize |
ClientIp | extend |
Url | extend |
Cyble Vision Alerts I2P Monitoring
#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.
'Triggers when I2P content is detected related to monitored keywords. Useful for identifying extremist narratives, radicalization indicators, or harmful ideological campaigns spreading through I2P hidden services.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Resource Development |
Rule Body
id: d1cdfb8d-12cd-4a29-8caf-ef4a35ad67ed
name: Cyble Vision Alerts I2P Monitoring
description: |
'Triggers when I2P content is detected related to monitored keywords. Useful for identifying extremist narratives, radicalization indicators, or harmful ideological campaigns spreading through I2P hidden services.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- ResourceDevelopment
relevantTechniques:
- T1585
query: |
Alerts_i2p
| where Service == "i2p"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: I2P Content Alert Hidden Service Activity for Keyword {{I2P_SearchKeyword}}
alertDescriptionFormat: |
Potentially sensitive or harmful content has been detected on the I2P network. URL {{I2P_URL}}. Content updated on {{I2P_UpdatedOn}}. The captured content may involve extremist ideology, radical discussions, or harmful narratives that require SOC review.
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
I2P_URL: I2P_URL
I2P_SearchKeyword: I2P_SearchKeyword
I2P_UpdatedOn: I2P_UpdatedOn
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: I2P_URL
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_i2p
Stage 2: where
| where Service == "i2p"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"i2p" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
Detect DNS queries reporting multiple errors from different clients - Static threshold based (ASIM DNS Solution)
#This rule creates an alert when multiple clients report errors for the same DNS query. This helps in identifying possible similar C2 communications originating from different clients. It utilizes ASIM normalization and is applied to any 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: 5b8344eb-fa28-4ac3-bcff-bc19d5d63089
name: Detect DNS queries reporting multiple errors from different clients - Static threshold based (ASIM DNS Solution)
description: |
'This rule creates an alert when multiple clients report errors for the same DNS query. This helps in identifying possible similar C2 communications originating from different clients. It utilizes [ASIM](https://aka.ms/AboutASIM) normalization and is applied to any source that supports the ASIM DNS schema.'
severity: Medium
status: Available
tags:
- Schema: ASimDns
SchemaVersion: 0.1.6
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1568
- T1573
- T1008
query: |
let lookback=1h;
let threshold = 2;
let errors = dynamic(['NXDOMAIN', 'SERVFAIL', 'REFUSED']);
_Im_Dns(starttime=ago(lookback))
| where EventResultDetails has_any (errors)
| summarize SrcIPs = make_set(SrcIpAddr, 100), Dvcs = make_set(Dvc, 100), ResourceIds = make_set(_ResourceId, 100) by DnsQuery, bin(TimeGenerated, 10min)
| where array_length(SrcIPs) >= threshold
| extend TotalIPs = array_length(SrcIPs),IPCountthreshold = threshold
| extend DomainName = strcat(split(DnsQuery, ".")[1], ".", split(DnsQuery, ".")[2])
| mv-expand SrcIPs
| extend SrcIP = tostring(SrcIPs)
| mv-expand Dvcs
| extend Dvc = tostring(Dvcs)
| mv-expand ResourceIds
| extend ResourceId = tostring(ResourceIds)
| extend Dvc = strcat(split(Dvc, ".")[0])
| summarize Start=min(TimeGenerated), End=max(TimeGenerated) by SrcIP, Dvc, ResourceId, DnsQuery, DomainName, SrcIPs = tostring(SrcIPs), IPCountthreshold = threshold, TotalIPs
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DnsQuery
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIP
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: ResourceId
- entityType: Url
fieldMappings:
- identifier: Url
columnName: DnsQuery
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
SrcIPs: SrcIPs
IPCountthreshold: IPCountthreshold
TotalIPs: TotalIPs
alertDetailsOverride:
alertDisplayNameFormat: "[Static threshold] Multiple errors for the same DNS query has been detected - '{{DnsQuery}}'"
alertDescriptionFormat: "Multiple errors were detected on different clients for the same DNS query. These unsuccessful responses can be an indication of C2 communication. \n\nThreshold for total clients reporting errors: '{{IPCountthreshold}}'\n\nCurrent count of clients reporting errors for this DNS query: '{{TotalIPs}}'\n\nClients requesting this DNSQuery include:\n\n'{{SrcIPs}}'"
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let lookback = 1h;
let threshold = 2;
let errors = dynamic(['NXDOMAIN', 'SERVFAIL', 'REFUSED']);
Stage 1: source
_Im_Dns(starttime=ago(lookback))
Stage 2: where
| where EventResultDetails has_any (errors)
Stage 3: summarize
| summarize SrcIPs = make_set(SrcIpAddr, 100), Dvcs = make_set(Dvc, 100), ResourceIds = make_set(_ResourceId, 100) by DnsQuery, bin(TimeGenerated, 10min)
Stage 4: where
| where array_length(SrcIPs) >= threshold
Stage 5: extend
| extend TotalIPs = array_length(SrcIPs),IPCountthreshold = threshold
Stage 6: extend
| extend DomainName = strcat(split(DnsQuery, ".")[1], ".", split(DnsQuery, ".")[2])
Stage 7: mv-expand
| mv-expand SrcIPs
Stage 8: extend
| extend SrcIP = tostring(SrcIPs)
Stage 9: mv-expand
| mv-expand Dvcs
Stage 10: extend
| extend Dvc = tostring(Dvcs)
Stage 11: mv-expand
| mv-expand ResourceIds
Stage 12: extend
| extend ResourceId = tostring(ResourceIds)
Stage 13: extend
| extend Dvc = strcat(split(Dvc, ".")[0])
Stage 14: summarize
| summarize Start=min(TimeGenerated), End=max(TimeGenerated) by SrcIP, Dvc, ResourceId, DnsQuery, DomainName, SrcIPs = tostring(SrcIPs), IPCountthreshold = threshold, TotalIPs
Stage 15: extend
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
Stage 16: extend
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
HostNameDomain =DomainIndex != -1substring(Dvc, (DomainIndex + 1))DvcIndicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventResultDetails | match |
| field:"Status" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsQuery | summarize |
DomainName | summarize |
Dvc | summarize |
End | summarize |
IPCountthreshold | summarize |
ResourceId | summarize |
SrcIP | summarize |
SrcIPs | summarize |
Start | summarize |
TotalIPs | summarize |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
Excessive Amount of Denied Connections from a Single Source
#'This creates an incident in the event that a single source IP address generates a excessive amount of denied connections.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 3d645a88-2724-41a7-adea-db74c439cf79
name: Excessive Amount of Denied Connections from a Single Source
description: |
'This creates an incident in the event that a single source IP address generates a excessive amount of denied connections.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1499
query: |
let threshold = 5000;
SophosXGFirewall
| where Log_Type =~ "Firewall" and Status =~ "Deny"
| summarize count() by Src_IP, bin(TimeGenerated,5m)
| where count_ > threshold
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Src_IP
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 5000;
Stage 1: source
SophosXGFirewall
Stage 2: where
| where Log_Type =~ "Firewall" and Status =~ "Deny"
Stage 3: summarize
| summarize count() by Src_IP, bin(TimeGenerated,5m)
Stage 4: where
| where count_ > threshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Log_Type | eq |
| field:"Log_Type" kind:eq value:"Firewall" |
Status | eq |
| field:"Status" kind:eq value:"Deny" |
count_ | gt |
| field:"count_" kind:gt value:"5000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Src_IP | summarize |
Guardian- Special PII Detection Policy Violation Detection
#'This alert creates an incident when Special PII Detection Policy Violation detected from the Guardian.'
Rule Body
id: 7e37bf0f-1a10-4a71-8207-19615ee75894
name: Guardian- Special PII Detection Policy Violation Detection
description: |
'This alert creates an incident when Special PII Detection Policy Violation detected from the Guardian.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
Guardian
| where PolicyViolatedControlFeature =~ 'Special PII Detection'
| where Severity =~ 'High'
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: Guardian- Special PII Detection Policy Violation detection
alertDescriptionFormat: |
This query detects Special PII Detection Policy Violation detected from the Guardian generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
alertTacticsColumnName: null
alertSeverityColumnName: Severity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Guardian
Stage 2: where
| where PolicyViolatedControlFeature =~ 'Special PII Detection'
Stage 3: where
| where Severity =~ 'High'
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PolicyViolatedControlFeature | eq |
| field:"PolicyViolatedControlFeature" kind:eq value:"Special PII Detection" |
Severity | eq |
| field:"Severity" kind:eq value:"High" |
License Removed
#Detects when the Veeam license is removed from Veeam Backup & Replication.
Rule Body
id: 8e4986ac-7388-4782-b389-0b0288123dbc
name: License Removed
description: Detects when the Veeam license is removed from Veeam Backup & Replication.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 24080\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 == 24080
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:"24080" |
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 |
McAfee ePO - Scanning engine disabled
#Detects when OAS scanning engine was disabled.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth | |
| Defense Impairment |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| McAfee-ePolicy-Orchestrator | Event ID 1127: On-access scan engine disabled |
Rule Body
id: 5223c1b8-75ef-4019-9076-a19b1ef3e5d1
name: McAfee ePO - Scanning engine disabled
description: |
'Detects when OAS scanning engine was disabled.'
severity: Low
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
- T1070
query: |
McAfeeEPOEvent
| where EventId == '1127'
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: HostCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
McAfeeEPOEvent
Stage 2: where
| where EventId == '1127'
Stage 3: extend
| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventId | eq |
| field:"EventId" kind:eq value:"1127" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostCustomEntity | extend |
IPCustomEntity | extend |
New User Assigned to Privileged Role
#Identifies when a privileged role is assigned to a new user. Any account eligible for a role is now being given privileged access. If the assignment is unexpected or into a role that isn't the responsibility of the account holder, investigate.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | any: Entra ID audit event (any operation) |
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions (Kusto)
- [Entra ID] Mass Privileged Role Change Activity Detected (Kusto)
- [Entra ID] Privilege Elevation Request Denied (Kusto)
- [Entra ID] Privileged Role Assigned to a New User (Kusto)
- [Entra ID] Privileged Role Assigned to User (Kusto)
- Account Elevated to New Role (Kusto)
- Admin promotion after Role Management Application Permission Grant (Kusto)
- App Assigned To Azure RBAC/Microsoft Entra Role (Sigma)
Rule Body
id: 050b9b3d-53d0-4364-a3da-1b678b8211ec
name: New User Assigned to Privileged Role
description: |
Identifies when a privileged role is assigned to a new user. Any account eligible for a role is now being given privileged access. If the assignment is unexpected or into a role that isn't the responsibility of the account holder, investigate.
severity: High
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- Persistence
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
// Define the start and end times based on input values
let starttime = now()-1h;
let endtime = now();
// Set a lookback period of 14 days
let lookback = starttime - 14d;
// Define a reusable function to query audit logs
let awsFunc = (start:datetime, end:datetime) {
AuditLogs
| where TimeGenerated between (start..end)
| where Category =~ "RoleManagement"
| where AADOperationType in ("Assign", "AssignEligibleRole")
| where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type in~ ("User", "ServicePrincipal")
| extend Target = iff(TargetResource.type =~ "ServicePrincipal", tostring(TargetResource.displayName), tostring(TargetResource.userPrincipalName)),
props = TargetResource.modifiedProperties
)
| mv-apply Property = props on
(
where Property.displayName =~ "Role.DisplayName"
| extend RoleName = trim('"', tostring(Property.newValue))
)
| where RoleName contains "Admin" and Result == "success"
};
// Query for audit events in the current day
let EventInfo_CurrentDay = awsFunc(starttime, endtime);
// Query for audit events in the historical period (lookback)
let EventInfo_historical = awsFunc(lookback, starttime);
// Find unseen events by performing a left anti-join
let EventInfo_Unseen = (EventInfo_CurrentDay
| join kind=leftanti(EventInfo_historical) on Target, RoleName, OperationName
);
// Extend and clean up the results
EventInfo_Unseen
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend Initiator = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)
// You can uncomment the lines below to filter out PIM activations
// | where Initiator != "MS-PIM" and Initiator != "MS-PIM-Fairfax"
// | summarize StartTime=min(TimeGenerated), EndTime=min(TimeGenerated) by OperationName, RoleName, Target, Initiator, Result
// Project specific columns and split them for further analysis
| project TimeGenerated, OperationName, RoleName, Target, Initiator, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, Result
| extend TargetName = tostring(split(Target,'@',0)[0]), TargetUPNSuffix = tostring(split(Target,'@',1)[0]), InitiatorName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatorUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Target
- identifier: Name
columnName: TargetName
- identifier: UPNSuffix
columnName: TargetUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatorName
- identifier: UPNSuffix
columnName: InitiatorUPNSuffix
- 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
Stages and Predicates
Parameters
let starttime = now()-1h;
let endtime = now();
let lookback = starttime - 14d;
let EventInfo_CurrentDay = awsFunc(starttime, endtime);
let EventInfo_historical = awsFunc(lookback, starttime);
let EventInfo_Unseen is inlined into the numbered stages below.
Let Binding: awsFunc
let awsFunc = (start:datetime, end:datetime) {
AuditLogs
| where TimeGenerated between (start..end)
| where Category =~ "RoleManagement"
| where AADOperationType in ("Assign", "AssignEligibleRole")
| where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type in~ ("User", "ServicePrincipal")
| extend Target = iff(TargetResource.type =~ "ServicePrincipal", tostring(TargetResource.displayName), tostring(TargetResource.userPrincipalName)),
props = TargetResource.modifiedProperties
)
| mv-apply Property = props on
(
where Property.displayName =~ "Role.DisplayName"
| extend RoleName = trim('"', tostring(Property.newValue))
)
| where RoleName contains "Admin" and Result == "success"
};
Stage 1: source
AuditLogs
Stage 2: where
where TimeGenerated >= "start" and TimeGenerated <= "end"
Stage 3: where
where Category =~ "RoleManagement"
Stage 4: where
where AADOperationType in ("Assign", "AssignEligibleRole")
Stage 5: where
where (ActivityDisplayName has "Add eligible member to role" or ActivityDisplayName has "Add member to role")
Stage 6: kusto:mv-apply
kusto:mv-apply TargetResource.type in~ ("ServicePrincipal", "User")
Stage 7: kusto:mv-apply
kusto:mv-apply Property.displayName =~ "Role.DisplayName"
Stage 8: where
where Result == "success" and RoleName contains "Admin"
Stage 9: join (negated)
join kind=leftanti (EventInfo_historical) on Target, RoleName, OperationName
Stage 10: extend (6 consecutive steps)
extend InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, InitiatingUserPrincipalName, Initiator
Stage 11: project
project InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, InitiatingUserPrincipalName, Initiator, OperationName, Result, RoleName, Target, TimeGenerated
Stage 12: extend
extend InitiatorName, InitiatorUPNSuffix, TargetName, TargetUPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AADOperationType | in |
| field:"AADOperationType" kind:in |
ActivityDisplayName | match |
| field:"azure_ad::activity_display_name" kind:match |
Category | eq |
| field:"Category" kind:eq value:"RoleManagement" |
Property.displayName | eq |
| field:"Property.displayName" kind:eq value:"Role.DisplayName" |
Result | eq |
| field:"Result" kind:eq value:"success" |
RoleName | contains |
| field:"RoleName" kind:contains value:"Admin" |
TargetResource.type | in |
| field:"TargetResource.type" kind:in |
TimeGenerated | ge |
| field:"TimeGenerated" kind:ge value:"start" |
TimeGenerated | le |
| field:"TimeGenerated" kind:le value:"end" |
Exclusions
The rule actively suppresses these predicates.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
InitiatingAadUserId | project |
InitiatingAppName | project |
InitiatingAppServicePrincipalId | project |
InitiatingIpAddress | project |
InitiatingUserPrincipalName | project |
Initiator | project |
OperationName | project |
Result | project |
RoleName | project |
Target | project |
TimeGenerated | project |
InitiatorName | extend |
InitiatorUPNSuffix | extend |
TargetName | extend |
TargetUPNSuffix | extend |
Pathlock TDnR - Table Parameter Setting Changes
#Detects changes to SAP table parameter settings, forwarded by Pathlock Threat Detection and Response. Table parameter modifications may affect security-relevant settings, buffering behavior, or data access controls that could be exploited to weaken the overall security posture.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c68
name: Pathlock TDnR - Table Parameter Setting Changes
kind: Scheduled
description: >-
Detects changes to SAP table parameter settings, forwarded by Pathlock Threat Detection
and Response. Table parameter modifications may affect security-relevant settings, buffering
behavior, or data access controls that could be exploited to weaken the overall security posture.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
query: |
Pathlock_TDnR_CL
| where DataSource == "TABLE_SETTINGS"
| 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 == "TABLE_SETTINGS"
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:"TABLE_SETTINGS" |
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 |
PE file dropped in Color Profile Folder
#This query looks for writes of PE files to C:\Windows\System32\spool\drivers\color. This is a common directory used by malware, as well as some legitimate programs, and writes of PE files to the folder should be monitored. Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Telemetry Coverage
Rule Body
id: f68a5046-b7eb-4f69-9519-1e99708bb9e0
name: PE file dropped in Color Profile Folder
description: |
'This query looks for writes of PE files to C:\Windows\System32\spool\drivers\color\.
This is a common directory used by malware, as well as some legitimate programs, and writes of PE files to the folder should be monitored.
Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceFileEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1203
tags:
- KNOTWEED
query: |
DeviceFileEvents
| where ActionType =~ "FileCreated"
| where FolderPath has "C:\\Windows\\System32\\spool\\drivers\\color\\"
| where FileName endswith ".exe" or FileName endswith ".dll"
entityMappings:
- entityType: File
fieldMappings:
- identifier: Name
columnName: FileName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
version: 1.0.1
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Pete Bryan
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Stage 1: source
DeviceFileEvents
Stage 2: where
| where ActionType =~ "FileCreated"
Stage 3: where
| where FolderPath has "C:\\Windows\\System32\\spool\\drivers\\color\\"
Stage 4: where
| where FileName endswith ".exe" or FileName endswith ".dll"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"FileCreated" |
FileName | ends_with |
| field:"file_name" kind:ends_with |
FolderPath | match |
| field:"TargetFilename" kind:match value:"C:\Windows\System32\spool\drivers\color\" |
Persistence Via Scheduled Tasks
#This query identifies binaries that run as a scheduled task, by looking at the parent process command line. Of the identified binaries running as scheduled tasks it finds suspicious binaries by looking at the file signature and global prevalence.
Known False Positives
- Some legitimate software also uses scheduled tasks, for example, for downloading periodic updates. If the software is unsigned or has a low global prevalence this might cause false positives.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence | |
| Privilege Escalation |
References
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action ProcessCreated: Process created |
Rule Body
let timeframe = 2*1d;
let default_global_prevalence = 0;
// Time to look back for same scheduled binary.
let lookback= 7d;
let ScheduledBinaries = (
DeviceProcessEvents
| where Timestamp >= ago(lookback)
| where ActionType =~ "ProcessCreated"
| where InitiatingProcessCommandLine startswith "svchost.exe -k netsvcs -p" and InitiatingProcessCommandLine contains "Schedule" // First argument after -p is censored with ** so can't look for the actual command line
);
let NewScheduledBinaries=(
ScheduledBinaries
| where Timestamp >= ago(lookback)
| summarize FirstSeen=min(Timestamp),LastSeen=max(Timestamp) by DeviceId, SHA1
| where LastSeen >= ago(timeframe)
| where FirstSeen >= ago(timeframe)
);
let NewScheduledBinaryExecution=(
ScheduledBinaries
| where ingestion_time() >= ago(timeframe)
| lookup kind=inner NewScheduledBinaries on DeviceId, SHA1
);
NewScheduledBinaryExecution
| summarize MachineCount=dcount(DeviceId) by SHA1
// Find the max 1000 least used binaries.
| top 1000 by MachineCount asc
// FileProfile is case-sensitive and works on lower-case hashes.
| extend SHA1=tolower(SHA1)
| invoke FileProfile(SHA1,1000)
| where not(ProfileAvailability =~ "Error")
| where coalesce(GlobalPrevalence,default_global_prevalence) < 100
| join NewScheduledBinaryExecution on SHA1
| summarize arg_max(Timestamp, *), Devices=make_set(DeviceName), MachineCount=dcount(DeviceName) by SHA1 // Gives the last execution with all details per SHA1.
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1d;
let default_global_prevalence = 0;
let lookback = 7d;
let ScheduledBinaries and let NewScheduledBinaryExecution are inlined into the numbered stages below.
Let Binding: NewScheduledBinaries
let NewScheduledBinaries = (
ScheduledBinaries
| where Timestamp >= ago(lookback)
| summarize FirstSeen=min(Timestamp),LastSeen=max(Timestamp) by DeviceId, SHA1
| where LastSeen >= ago(timeframe)
| where FirstSeen >= ago(timeframe)
);
Stage 1: source
let ScheduledBinaries
Stage 2: source
let NewScheduledBinaries
Stage 3: source
let NewScheduledBinaryExecution
Stage 4: source
DeviceProcessEvents
Stage 5: where
where Timestamp >= ago(604800s)
Stage 6: where
where ActionType =~ "ProcessCreated"
Stage 7: where
where InitiatingProcessCommandLine contains "Schedule" and InitiatingProcessCommandLine startswith "svchost.exe -k netsvcs -p"
Stage 8: where
where ...
Stage 9: kusto:lookup
lookup kind=inner (NewScheduledBinaries) on DeviceId, SHA1
Stage 10: summarize
summarize MachineCount by SHA1
Stage 11: top
top
Stage 12: extend
extend SHA1
Stage 13: invoke
invoke
Stage 14: where
where not (ProfileAvailability =~ "Error")
Stage 15: where
where GlobalPrevalence < 100
Stage 16: join
join (NewScheduledBinaryExecution) on SHA1
Stage 17: summarize
summarize Devices, MachineCount by SHA1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"ProcessCreated" |
GlobalPrevalence | lt |
| field:"GlobalPrevalence" kind:lt value:"100" |
InitiatingProcessCommandLine | contains |
| field:"InitiatingProcessCommandLine" kind:contains value:"Schedule" |
InitiatingProcessCommandLine | starts_with |
| field:"InitiatingProcessCommandLine" kind:starts_with value:"svchost.exe -k netsvcs -p" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ProfileAvailability | eq | Error | excludes:ProfileAvailability field:"ProfileAvailability" value:"Error" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Devices | summarize |
MachineCount | summarize |
SHA1 | summarize |
Privileged Account Permissions Changed
#Detects changes to permissions assigned to admin users. Threat actors may try and increase permission scope by adding additional roles to already privileged accounts. Review any modifications to ensure they were made legitimately. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#changes-to-privileged-accounts
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions (Kusto)
- [Entra ID] Mass Privileged Role Change Activity Detected (Kusto)
- [Entra ID] Privilege Elevation Request Denied (Kusto)
- [Entra ID] Privileged Role Assigned to a New User (Kusto)
- [Entra ID] Privileged Role Assigned to User (Kusto)
- Account Elevated to New Role (Kusto)
- Admin promotion after Role Management Application Permission Grant (Kusto)
- App Assigned To Azure RBAC/Microsoft Entra Role (Sigma)
Rule Body
id: 0433c8a3-9aa6-4577-beef-2ea23be41137
name: Privileged Account Permissions Changed
description: |
'Detects changes to permissions assigned to admin users. Threat actors may try and increase permission scope by adding additional roles to already privileged accounts.
Review any modifications to ensure they were made legitimately.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#changes-to-privileged-accounts'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- connectorId: BehaviorAnalytics
dataTypes:
- IdentityInfo
queryFrequency: 1d
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
let admin_users = (IdentityInfo
| where TimeGenerated > ago(2d)
| summarize arg_max(TimeGenerated, *) by AccountUPN
| where AssignedRoles contains "admin" or GroupMembership has "Admin"
| summarize by tolower(AccountUPN));
AuditLogs
| where Category =~ "RoleManagement"
| where OperationName has "Add eligible member"
| extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| where tolower(TargetUserPrincipalName) in (admin_users)
| extend TargetAadUserId = tostring(TargetResources[0].id)
| extend Group = tostring(TargetResources[0].displayName)
| extend RoleAddedTo = iif(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, Group)
| extend mod_props = TargetResources[0].modifiedProperties
| 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 RoleAddedBy = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)
| mv-expand mod_props
| where mod_props.displayName == "Role.DisplayName"
| extend UserAgent = tostring(AdditionalDetails[0].value)
| extend RoleAdded = tostring(parse_json(tostring(mod_props.newValue)))
| extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1])
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| project-reorder TimeGenerated, OperationName, TargetUserPrincipalName, RoleAddedTo, RoleAdded, RoleAddedBy, InitiatingUserPrincipalName, InitiatingAppName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUserPrincipalName
- identifier: Name
columnName: TargetAccountName
- identifier: UPNSuffix
columnName: TargetAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: TargetAadUserId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
version: 1.0.7
kind: Scheduled
Stages and Predicates
Let Binding: admin_users
let admin_users = (IdentityInfo
| where TimeGenerated > ago(2d)
| summarize arg_max(TimeGenerated, *) by AccountUPN
| where AssignedRoles contains "admin" or GroupMembership has "Admin"
| summarize by tolower(AccountUPN));
Stage 1: source
let admin_users
Stage 2: source
AuditLogs
Stage 3: where
where Category =~ "RoleManagement"
Stage 4: where
where OperationName has "Add eligible member"
Stage 5: extend
extend TargetUserPrincipalName
Stage 6: where
where TargetUserPrincipalName == "admin_users"
Stage 7: extend (10 consecutive steps)
extend Group, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIPAddress, InitiatingUserPrincipalName, RoleAddedBy, RoleAddedTo, TargetAadUserId, mod_props
Stage 8: mv-expand
mv-expand mod_props
Stage 9: where
where mod_props.displayName == "Role.DisplayName"
Stage 10: extend (4 consecutive steps)
extend InitiatingAccountName, InitiatingAccountUPNSuffix, RoleAdded, TargetAccountName, TargetAccountUPNSuffix, UserAgent
Stage 11: project-reorder
project-reorder
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"RoleManagement" |
OperationName | match |
| field:"OperationName" kind:match value:"Add eligible member" |
TargetUserPrincipalName | in |
| field:"TargetUserPrincipalName" kind:in value:"admin_users" |
mod_props.displayName | eq |
| field:"mod_props.displayName" kind:eq value:"Role.DisplayName" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TargetUserPrincipalName | extend |
TargetAadUserId | extend |
Group | extend |
RoleAddedTo | extend |
mod_props | extend |
InitiatingAppName | extend |
InitiatingAppServicePrincipalId | extend |
InitiatingUserPrincipalName | extend |
InitiatingAadUserId | extend |
InitiatingIPAddress | extend |
RoleAddedBy | extend |
UserAgent | extend |
RoleAdded | extend |
TargetAccountName | extend |
TargetAccountUPNSuffix | extend |
InitiatingAccountName | extend |
InitiatingAccountUPNSuffix | extend |
PulseConnectSecure - CVE-2021-22893 Possible Pulse Connect Secure RCE Vulnerability Attack
#'This query identifies exploitation attempts using Pulse Connect Secure(PCS) vulnerability (CVE-2021-22893) to the VPN server'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: d0c82b7f-40b2-4180-a4d6-7aa0541b7599
name: PulseConnectSecure - CVE-2021-22893 Possible Pulse Connect Secure RCE Vulnerability Attack
description: |
'This query identifies exploitation attempts using Pulse Connect Secure(PCS) vulnerability (CVE-2021-22893) to the VPN server'
severity: High
requiredDataConnectors:
- connectorId: PulseConnectSecure
dataTypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let threshold = 3;
PulseConnectSecure
| where Messages contains "Unauthenticated request url /dana-na/"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Source_IP
| where count_ > threshold
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Source_IP
version: 1.0.2
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Sittikorn S
support:
tier: Community
categories:
domains: [ "Security - Threat Intelligence" ]
Stages and Predicates
Parameters
let threshold = 3;
Stage 1: source
PulseConnectSecure
Stage 2: where
| where Messages contains "Unauthenticated request url /dana-na/"
Stage 3: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by Source_IP
Stage 4: where
| where count_ > threshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Messages | contains |
| field:"Messages" kind:contains value:"Unauthenticated request url /dana-na/" |
count_ | gt |
| field:"count_" kind:gt value:"3" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EndTime | summarize |
Source_IP | summarize |
StartTime | summarize |
Sensitive Data Discovered in the Last 24 Hours
#'Identifies all classifications that have been detected on assets during a scan by Microsoft Purview within the last 24 hours.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: 7ae7e8b0-07e9-43cb-b783-b04082f09060
name: Sensitive Data Discovered in the Last 24 Hours
description: |
'Identifies all classifications that have been detected on assets during a scan by Microsoft Purview within the last 24 hours.'
severity: Informational
requiredDataConnectors:
- connectorId: MicrosoftAzurePurview
dataTypes:
- PurviewDataSensitivityLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1087
query: |
PurviewDataSensitivityLogs
| where Classification != "[]"
| where TimeGenerated > ago(24h)
entityMappings:
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: SourcePath
- entityType: File
fieldMappings:
- identifier: Name
columnName: AssetName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: PurviewAccountName
customDetails:
AssetName: AssetName
Classification: Classification
AssetPath: AssetPath
SourceRegion: SourceRegion
PurviewAccount: PurviewAccountName
LastScanTime: AssetLastScanTime
alertDetailsOverride:
alertDisplayNameFormat: 'Sensitive Data Discovered in the Last 24 Hours by Microsoft Purview'
alertDescriptionFormat: 'Within the last 24 hours, Microsoft Purview scanned assets that contained classifications. The classifications discovered include {{Classification}}.'
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
PurviewDataSensitivityLogs
Stage 2: where
| where Classification != "[]"
Stage 3: where
| where TimeGenerated > ago(24h)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Classification | ne |
| field:"Classification" kind:ne value:"[]" |
UniFi Site Manager: WiFi quality degraded (high TX retry)
#Identifies when the site-wide WiFi TX retry rate exceeds 5 percent, which may indicate RF interference, channel congestion, or active wireless attack.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: f5130582-9e0e-cb47-af0b-dcd8b261fa04
name: "UniFi Site Manager: WiFi quality degraded (high TX retry)"
description: |
Identifies when the site-wide WiFi TX retry rate exceeds 5 percent, which may indicate RF interference, channel congestion, or active wireless attack.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: UniFiSiteManagerConnectorDefinition
dataTypes:
- Unifi_SiteManager_Sites_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1498
subTechniques:
- T1498.001
query: |
Unifi_SiteManager_Sites_CL
| where TimeGenerated > ago(1h)
| extend txRetry = WifiTxRetryPct
| where txRetry > 5.0
| summarize arg_max(TimeGenerated, *) by SiteId, SiteName
| extend Activity = strcat('TX retry rate ', round(txRetry, 1), '% (>5% threshold)')
| project TimeGenerated, SiteId, SiteName, Activity, TxRetryPct = round(txRetry, 1)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SiteId
- identifier: DnsDomain
columnName: SiteName
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.1
Stages and Predicates
Stage 1: source
Unifi_SiteManager_Sites_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: extend
| extend txRetry = WifiTxRetryPct
Stage 4: where
| where txRetry > 5.0
Stage 5: summarize
| summarize arg_max(TimeGenerated, *) by SiteId, SiteName
Stage 6: extend
| extend Activity = strcat('TX retry rate ', round(txRetry, 1), '% (>5% threshold)')
Stage 7: project
| project TimeGenerated, SiteId, SiteName, Activity, TxRetryPct = round(txRetry, 1)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
txRetry | gt |
| field:"WifiTxRetryPct" kind:gt value:"5.0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity | project |
SiteId | project |
SiteName | project |
TimeGenerated | project |
TxRetryPct | project |
Zscaler - ZPA connections from new country
#'Detects ZPA connections from new country.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: c4902121-7a7e-44d1-810b-88d26db622ff
name: Zscaler - ZPA connections from new country
description: |
'Detects ZPA connections from new country.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
datatypes:
- ZPA_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
let listCountries =
ZPAEvent
| where TimeGenerated > ago(14d)
| where DvcAction == 'open'
| summarize ListofCountries = make_set(SrcGeoCountry) by DstUserName
| project ListofCountries;
ZPAEvent
| where isnotempty(SrcGeoCountry)
| where SrcGeoCountry !in (listCountries)
| summarize EventCount = count() by DstUserName, SrcGeoCountry
| project-away EventCount
| extend AccountCustomEntity = DstUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Let Binding: listCountries
let listCountries = ZPAEvent
| where TimeGenerated > ago(14d)
| where DvcAction == 'open'
| summarize ListofCountries = make_set(SrcGeoCountry) by DstUserName
| project ListofCountries;
Stage 1: source
ZPAEvent
Stage 2: where
| where isnotempty(SrcGeoCountry)
Stage 3: where
| where SrcGeoCountry !in (listCountries)
Stage 4: summarize
| summarize EventCount = count() by DstUserName, SrcGeoCountry
Stage 5: project-away
| project-away EventCount
Stage 6: extend
| extend AccountCustomEntity = DstUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
SrcGeoCountry | is_not_null | field:"SrcGeoCountry" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcGeoCountry | eq | listCountries | excludes:SrcGeoCountry field:"SrcGeoCountry" value:"listCountries" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | summarize |
SrcGeoCountry | summarize |
AccountCustomEntity | extend |