Detection rules › Kusto

Kusto rules: 33

Cisco WSA - Multiple errors to resource from risky category

#
Status
available
Severity
medium
Time window
5m
Group by
SrcUserName, UrlOriginal
Source
github.com/Azure/Azure-Sentinel

'Detects multiple connection errors to resource from risky category.'

MITRE ATT&CK Coverage

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcUserNamesummarize
UrlOriginalsummarize
AccountCustomEntityextend
URLCustomEntityextend

Clearing of forensic evidence from event logs using wevtutil

#
Status
available
Severity
high
Time window
5m
Group by
DeviceId, DeviceName
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
Stealth

Telemetry Coverage

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 =
ifDeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))
elseDeviceName

Stage 6: extend

| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =
ifDeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))
else""

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
LogClearCountgt
  • 10 transforms: number
field:"LogClearCount" kind:gt value:"10"
ProcessCommandLinematch
  • CL transforms: term
  • WEVTUTIL transforms: term
field:"CommandLine" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
ClearedLogListsummarize
DeviceIdsummarize
DeviceNamesummarize
LogClearCountsummarize
HostNameextend
DnsDomainextend

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.

Status
available
Severity
high
Time window
5h
Source
github.com/Azure/Azure-Sentinel

'Display incidents in which highly sensitive governance information was placed at risk by user sharing.'

MITRE ATT&CK Coverage

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

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.

Status
available
Severity
medium
Time window
5h
Source
github.com/Azure/Azure-Sentinel

'Display incidents in which medium sensitivity legal information was placed at risk by user sharing.'

MITRE ATT&CK Coverage

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

CTE - Risky Device (Multiple IOC URL Hits)

#
Severity
medium
Time window
7d
Group by
DeviceId, DeviceOs, DeviceUser, HostName, TenantId
Source
github.com/Azure/Azure-Sentinel

A single device has repeatedly hit known-malicious (threat-intel-matched) URLs, indicating persistent compromise risk or repeated user exposure.

MITRE ATT&CK Coverage

TacticTechniques
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 =
ifisnotempty(tostring(d.hostname))tostring(d.hostname)
elsestrcat("cte-device-", substring(EntityId, 0, 8))
RuleName =
ifisnotempty(tostring(d.ruleName))tostring(d.ruleName)
else"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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
BlockedUrlssummarize
ClientIpssummarize
DeviceIdsummarize
DeviceOssummarize
DeviceUsersummarize
HostNamesummarize
IOC_Hitssummarize
LastSeensummarize
TenantIdsummarize
ClientIpextend
Urlextend

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.

Status
available
Severity
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'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

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

Detect DNS queries reporting multiple errors from different clients - Static threshold based (ASIM DNS Solution)

#
Status
available
Severity
medium
Time window
10m
Group by
DnsQuery, DomainName, Dvc, IPCountthreshold, ResourceId, SrcIP, SrcIPs, TotalIPs
Source
github.com/Azure/Azure-Sentinel

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

Telemetry Coverage

ProviderRecord / event type
SysmonEvent 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 =
ifDomainIndex != -1substring(Dvc, (DomainIndex + 1))
elseDvc

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventResultDetailsmatch
  • NXDOMAIN transforms: term
  • REFUSED transforms: term
  • SERVFAIL transforms: term
field:"Status" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsQuerysummarize
DomainNamesummarize
Dvcsummarize
Endsummarize
IPCountthresholdsummarize
ResourceIdsummarize
SrcIPsummarize
SrcIPssummarize
Startsummarize
TotalIPssummarize
DomainIndexextend
HostNameextend
HostNameDomainextend

Excessive Amount of Denied Connections from a Single Source

#
Status
available
Severity
medium
Time window
5m
Group by
Src_IP
Source
github.com/Azure/Azure-Sentinel

'This creates an incident in the event that a single source IP address generates a excessive amount of denied connections.'

MITRE ATT&CK Coverage

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Src_IPsummarize

Guardian- Special PII Detection Policy Violation Detection

#
Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'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.

License Removed

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
User Nameproject

McAfee ePO - Scanning engine disabled

#
Status
available
Severity
low
Time window
1h
Source
github.com/Azure/Azure-Sentinel

Detects when OAS scanning engine was disabled.

MITRE ATT&CK Coverage

TacticTechniques
Stealth
Defense Impairment

Telemetry Coverage

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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostCustomEntityextend
IPCustomEntityextend

New User Assigned to Privileged Role

#
Status
available
Severity
high
Time window
14d
Group by
OperationName, RoleName, Target
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
Persistence

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

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 used in Stages 1, 9

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.

FieldKindValuesSearch
AADOperationTypein
  • Assign corpus 6 (kusto 6)
  • AssignEligibleRole corpus 5 (kusto 5)
field:"AADOperationType" kind:in
ActivityDisplayNamematch
  • Add eligible member to role transforms: term corpus 9 (kusto 9)
  • Add member to role transforms: term corpus 9 (kusto 9)
field:"azure_ad::activity_display_name" kind:match
Categoryeq
  • RoleManagement corpus 17 (kusto 16, sigma 1)
field:"Category" kind:eq value:"RoleManagement"
Property.displayNameeq
  • Role.DisplayName transforms: kusto_dynamic_path
field:"Property.displayName" kind:eq value:"Role.DisplayName"
Resulteq
  • success
field:"Result" kind:eq value:"success"
RoleNamecontains
  • Admin
field:"RoleName" kind:contains value:"Admin"
TargetResource.typein
  • ServicePrincipal transforms: kusto_dynamic_path
  • User transforms: kusto_dynamic_path
field:"TargetResource.type" kind:in
TimeGeneratedge
  • start
field:"TimeGenerated" kind:ge value:"start"
TimeGeneratedle
  • end
field:"TimeGenerated" kind:le value:"end"

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
InitiatingAadUserIdproject
InitiatingAppNameproject
InitiatingAppServicePrincipalIdproject
InitiatingIpAddressproject
InitiatingUserPrincipalNameproject
Initiatorproject
OperationNameproject
Resultproject
RoleNameproject
Targetproject
TimeGeneratedproject
InitiatorNameextend
InitiatorUPNSuffixextend
TargetNameextend
TargetUPNSuffixextend

Pathlock TDnR - Table Parameter Setting Changes

#
Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

PE file dropped in Color Profile Folder

#
Severity
medium
Time window
1d
Author
Pete Bryan
Source
github.com/Azure/Azure-Sentinel

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

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.

FieldKindValuesSearch
ActionTypeeq
  • FileCreated
field:"ActionType" kind:eq value:"FileCreated"
FileNameends_with
  • .dll corpus 7 (splunk 6, kusto 1)
  • .exe corpus 2 (kusto 2)
field:"file_name" kind:ends_with
FolderPathmatch
  • C:\Windows\System32\spool\drivers\color\ transforms: term
field:"TargetFilename" kind:match value:"C:\Windows\System32\spool\drivers\color\"

Persistence Via Scheduled Tasks

#
Group by
DeviceId, SHA1
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

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

References

Telemetry Coverage

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 used in Stages 2, 9

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.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ProfileAvailabilityeqErrorexcludes:ProfileAvailability field:"ProfileAvailability" value:"Error"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Devicessummarize
MachineCountsummarize
SHA1summarize

Privileged Account Permissions Changed

#
Severity
medium
Time window
2d
Group by
AccountUPN
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
Privilege Escalation

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

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 used in Stages 1, 6

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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetUserPrincipalNameextend
TargetAadUserIdextend
Groupextend
RoleAddedToextend
mod_propsextend
InitiatingAppNameextend
InitiatingAppServicePrincipalIdextend
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIPAddressextend
RoleAddedByextend
UserAgentextend
RoleAddedextend
TargetAccountNameextend
TargetAccountUPNSuffixextend
InitiatingAccountNameextend
InitiatingAccountUPNSuffixextend

PulseConnectSecure - CVE-2021-22893 Possible Pulse Connect Secure RCE Vulnerability Attack

#
Severity
high
Time window
1h
Group by
Source_IP
Author
Sittikorn S
Source
github.com/Azure/Azure-Sentinel

'This query identifies exploitation attempts using Pulse Connect Secure(PCS) vulnerability (CVE-2021-22893) to the VPN server'

MITRE ATT&CK Coverage

TacticTechniques
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.

FieldKindValuesSearch
Messagescontains
  • Unauthenticated request url /dana-na/
field:"Messages" kind:contains value:"Unauthenticated request url /dana-na/"
count_gt
  • 3 transforms: number
field:"count_" kind:gt value:"3"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EndTimesummarize
Source_IPsummarize
StartTimesummarize

Sensitive Data Discovered in the Last 24 Hours

#
Severity
informational
Time window
1d
Source
github.com/Azure/Azure-Sentinel

'Identifies all classifications that have been detected on assets during a scan by Microsoft Purview within the last 24 hours.'

MITRE ATT&CK Coverage

TacticTechniques
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.

UniFi Site Manager: WiFi quality degraded (high TX retry)

#
Status
available
Severity
low
Time window
1h
Group by
SiteId, SiteName
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
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.

FieldKindValuesSearch
txRetrygt
  • 5.0 transforms: number
field:"WifiTxRetryPct" kind:gt value:"5.0"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Activityproject
SiteIdproject
SiteNameproject
TimeGeneratedproject
TxRetryPctproject

Zscaler - ZPA connections from new country

#
Status
available
Severity
medium
Time window
14d
Group by
DstUserName, SrcGeoCountry
Source
github.com/Azure/Azure-Sentinel

'Detects ZPA connections from new country.'

MITRE ATT&CK Coverage

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 used in Stage 3

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.

FieldKindValuesSearch
SrcGeoCountryis_not_null
  • (no value, null check)
field:"SrcGeoCountry" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
SrcGeoCountryeqlistCountriesexcludes:SrcGeoCountry field:"SrcGeoCountry" value:"listCountries"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNamesummarize
SrcGeoCountrysummarize
AccountCustomEntityextend

Back to all Kusto rules