Detection rules › Kusto

Kusto rules: 0w

1Password - Service account integration token adjustment

#
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

This will alert when a service account integration token has been created, renamed, verified, or revoked. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same. Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Stealth

Telemetry Coverage

Rule Body

id: d54a3cf9-6169-449c-83f1-e7def3359702
name: 1Password - Service account integration token adjustment
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when a service account integration token has been created, renamed, verified, or revoked. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same.

  Ref: https://1password.com/
  Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
  - connectorId: 1Password
    dataTypes:
      - OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1134
query: |-
  OnePasswordEventLogs_CL
  | where log_source == "auditevents"
  | where action has_any("create", "trename", "tverify", "trevoke")
  | where object_type == "satoken"
  | extend
      ActorUsername = actor_details.email
      , SrcIpAddr = session.ip
subTechniques:
- T1134.003
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ActorUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr

Stages and Predicates

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where log_source == "auditevents"

Stage 3: where

| where action has_any("create", "trename", "tverify", "trevoke")

Stage 4: where

| where object_type == "satoken"

Stage 5: extend

| extend
    ActorUsername = actor_details.email
    , SrcIpAddr = session.ip

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actionmatch
  • create transforms: term
  • trename transforms: term
  • trevoke transforms: term
  • tverify transforms: term
field:"action" kind:match
log_sourceeq
  • auditevents corpus 12 (kusto 12)
field:"log_source" kind:eq value:"auditevents"
object_typeeq
  • satoken
field:"object_type" kind:eq value:"satoken"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorUsernameextend
SrcIpAddrextend

Alsid Password issues

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

'Searches for triggered Indicators of Exposures related to password issues'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 472b7cf4-bf1a-4061-b9ab-9fe4894e3c17
name: Alsid Password issues
description: |
    'Searches for triggered Indicators of Exposures related to password issues'
severity: Low
requiredDataConnectors:
  - connectorId: AlsidForAD
    dataTypes:
      - AlsidForADLog_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let SeverityTable=datatable(Severity:string,Level:int) [
  "low", 1,
  "medium", 2,
  "high", 3,
  "critical", 4
  ];
  let codeNameList = datatable(Codename:string)["C-CLEARTEXT-PASSWORD", "C-PASSWORD-DONT-EXPIRE", "C-USER-REVER-PWDS", "C-PASSWORD-POLICY", "C-USER-PASSWORD", "C-KRBTGT-PASSWORD", "C-AAD-SSO-PASSWORD", "C-REVER-PWD-GPO"];
  afad_parser
  | where MessageType == 0 and Codename in~ (codeNameList)
  | lookup kind=leftouter SeverityTable on Severity
  | order by Level
  | extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.1
kind: Scheduled

Stages and Predicates

Let Binding: SeverityTable used in Stage 3

let SeverityTable = datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];

Let Binding: codeNameList used in Stage 2

let codeNameList = datatable(Codename:string)["C-CLEARTEXT-PASSWORD", "C-PASSWORD-DONT-EXPIRE", "C-USER-REVER-PWDS", "C-PASSWORD-POLICY", "C-USER-PASSWORD", "C-KRBTGT-PASSWORD", "C-AAD-SSO-PASSWORD", "C-REVER-PWD-GPO"];

Stage 1: source

afad_parser

Stage 2: where

| where MessageType == 0 and Codename in~ (codeNameList)

Stage 3: kusto:lookup

lookup kind=leftouter (SeverityTable) on Severity

Stage 4: sort

sort by Level

Stage 5: extend

extend DnsDomain, HostName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

BloodHound Attack Path Finding - WriteOwner Privileges on Tier Zero Objects

#

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
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - WriteOwner Privileges on Tier Zero Objects

Rule Body

id: 924e60be-38af-4a33-98d4-45f2b1643094
name: BloodHound Attack Path Finding - WriteOwner Privileges on Tier Zero Objects
description: BloodHound Attack Path Finding - WriteOwner Privileges on Tier Zero Objects
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "T0WriteOwner"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "T0WriteOwner"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • T0WriteOwner
field:"Finding" kind:eq value:"T0WriteOwner"

Certified Pre-Owned - backup of CA private key - rule 2

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

This query identifies someone that performs a backup of they CA key.

MITRE ATT&CK Coverage

TacticTechniques
Stealth

Telemetry Coverage

Rule Body

id: 88f8fbc0-345d-458e-85f6-f73921d5ef50
name: Certified Pre-Owned - backup of CA private key - rule 2
description: |
  This query identifies someone that performs a backup of they CA key.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1036
query: |
  SecurityEvent
  // Fill in the machine name of your CA.
  | where EventID == 5059 and Computer contains "<YOUR CA MACHINE NAME>"
  | where EventData contains "%%2499" and EventData contains "%%2464"
  | extend EventData=parse_xml(EventData)
  | mv-apply d=EventData.EventData.Data on
  (
      where d["@Name"]=="KeyName"
      | project KeyName=tostring(d["#text"])
  )
  | mv-apply d=EventData.EventData.Data on
  (
      where d["@Name"]=="SubjectUserName"
      | project SubjectUserName=tostring(d["#text"])
  )
  | parse Account with "<YOUR DOMAIN NAME>\\" CleanAccount "$"
  | where not(Computer startswith CleanAccount)

entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5059 and Computer contains "<YOUR CA MACHINE NAME>"

Stage 3: where

| where EventData contains "%%2499" and EventData contains "%%2464"

Stage 4: extend

| extend EventData=parse_xml(EventData)

Stage 5: kusto:mv-apply

| mv-apply d=EventData.EventData.Data on
(
    where d["@Name"]=="KeyName"
    | project KeyName=tostring(d["#text"])
)

Stage 6: kusto:mv-apply

| mv-apply d=EventData.EventData.Data on
(
    where d["@Name"]=="SubjectUserName"
    | project SubjectUserName=tostring(d["#text"])
)

Stage 7: parse

| parse Account with "<YOUR DOMAIN NAME>\\" CleanAccount "$"

Stage 8: where

| where not(Computer startswith CleanAccount)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Computercontains
  • <YOUR CA MACHINE NAME> corpus 2 (kusto 2)
field:"Computer" kind:contains value:"<YOUR CA MACHINE NAME>"
EventDatacontains
  • %%2464
  • %%2499 corpus 2 (kusto 2)
field:"EventData" kind:contains
EventIDeq
  • 5059
field:"EventID" kind:eq value:"5059"
d["@Name"]eq
  • KeyName transforms: kusto_dynamic_path
  • SubjectUserName transforms: kusto_dynamic_path

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
Computerstarts_withCleanAccountexcludes:Computer field:"Computer" value:"CleanAccount"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventDataextend

Detect non-admin requesting token for admin applications

#
Group by
$left.UserId, $right.AccountObjectId, AccountUPN, AppDisplayName, IPAddress, ResultType, UserId, UserPrincipalName
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This rule detects sign-in attempts from non-admin users to admin applications in Entra ID.

MITRE ATT&CK Coverage

TacticTechniques
Execution

References

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

let ITAccounts=(_GetWatchlist('ITAccounts') | summarize make_set(ITAccounts));
// Materialize Dataset
let DataSetMat= materialize (SigninLogs
| where TimeGenerated > ago(1h)
| where AppDisplayName has_any ("PowerShell", "CLI", "Command Line", "Management Shell")
// Get successful and failed due to no assignment logins
| where ResultType in ("0", "50105")
| summarize max(TimeGenerated) by UserPrincipalName, AppDisplayName, IPAddress, UserId, ResultType
// join IdentityInfo to get more information
| join kind=leftouter (IdentityInfo | where TimeGenerated > ago(14d) | summarize arg_max(TimeGenerated, *) by AccountObjectId ) on $left.UserId == $right.AccountObjectId
// exclude Accounts with Assigned Roles
| where array_length(AssignedRoles) == 0
// exclude known IT personnel Departments
| where Department !has "it" and Department !has "ict" and Department !has "operations"
// exclude service accounts
| where JobTitle != "Service Account");
// exclude IT accounts
let FIL= (DataSetMat
| extend ITAccounts= toscalar(ITAccounts)
| mv-expand ITAccounts
| where AccountUPN contains ITAccounts or AccountDisplayName contains ITAccounts);
DataSetMat
// exclude service accounts
| join kind=leftanti FIL on AccountUPN
| distinct  max_TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, JobTitle, Department, UserId, ResultType

Stages and Predicates

let DataSetMat is inlined into the numbered stages below.

Let Binding: ITAccounts

let ITAccounts = (_GetWatchlist('ITAccounts') | summarize make_set(ITAccounts));

Let Binding: FIL used in Stage 11

let FIL = (DataSetMat
| extend ITAccounts= toscalar(ITAccounts)
| mv-expand ITAccounts
| where AccountUPN contains ITAccounts or AccountDisplayName contains ITAccounts);

Stage 1: source

let DataSetMat

Stage 2: source

SigninLogs

Stage 3: where

where TimeGenerated > ago(3600s)

Stage 4: where

where (AppDisplayName has "PowerShell" or AppDisplayName has "CLI" or AppDisplayName has "Command Line" or AppDisplayName has "Management Shell")

Stage 5: where

where ResultType in (0, 50105)

Stage 6: summarize

summarize by UserPrincipalName, AppDisplayName, IPAddress, UserId, ResultType

Stage 7: join

join kind=leftouter (IdentityInfo) on $left.UserId, $right.AccountObjectId

Stage 8: where

where AssignedRoles == 0

Stage 9: where

where not (Department has "ict") and not (Department has "it") and not (Department has "operations")

Stage 10: where

where JobTitle != "Service Account"

Stage 11: join (negated)

join kind=leftanti (FIL) on AccountUPN

Stage 12: distinct

distinct AppDisplayName, Department, IPAddress, JobTitle, ResultType, UserId, UserPrincipalName, max_TimeGenerated

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AppDisplayNamematch
  • CLI transforms: term
  • Command Line transforms: term
  • Management Shell transforms: term
  • PowerShell transforms: term
field:"AppDisplayName" kind:match
Departmentmatch
  • ict transforms: term
  • it transforms: term
  • operations transforms: term
field:"Department" kind:match
JobTitlene
  • Service Account
field:"JobTitle" kind:ne value:"Service Account"
ResultTypein
  • 0
  • 50105
field:"ResultType" kind:in

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AppDisplayNamesummarize
IPAddresssummarize
ResultTypesummarize
UserIdsummarize
UserPrincipalNamesummarize

GitLab - Brute-force Attempts

#
Status
available
Severity
medium
Time window
1h
Group by
User
Source
github.com/Azure/Azure-Sentinel

'This query relies on GitLab Application Logs to get failed logins to highlight brute-force attempts from different IP addresses in a short space of time.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 2238d13a-cf05-4973-a83f-d12a25dbb153
name: GitLab - Brute-force Attempts
description: |
  'This query relies on GitLab Application Logs to get failed logins to highlight brute-force attempts from different IP addresses in a short space of time.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    dataTypes: 
      - Syslog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let LearningPeriod = 7d; 
  let EndLearningTime = now();
  let BinTime = 1h; 
  let RunTime = 1h; 
  let MinThreshold = 3.0; 
  let GitLabFailedLogins = (GitLabApp
  | where FailedLogin == 1
  | parse kind=regex Message with "Failed Login: username=" User "ip=" IpAddress 
  | project TimeGenerated, EventTime, Computer, User, HostName, HostIP, IpAddress);
  GitLabFailedLogins 
  | where EventTime between (ago(LearningPeriod) .. EndLearningTime) 
  | summarize FailedLoginsCountInBinTime = count() by User, bin(EventTime, BinTime) 
  | summarize AvgOfFailedLoginsInLearning = avg(FailedLoginsCountInBinTime), StdOfFailedLoginsInLearning = stdev(FailedLoginsCountInBinTime) by User 
  | extend LearningThreshold = max_of(AvgOfFailedLoginsInLearning, MinThreshold) 
  | join kind=innerunique ( GitLabFailedLogins 
  | summarize FailedLoginsCountInRunTime = count() by User, IpAddress, EventTime = bin(EventTime, BinTime) ) on User 
  | where FailedLoginsCountInRunTime >= LearningThreshold
  | project User, IpAddress, EventTime, FailedLoginsCountInRunTime, LearningThreshold
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: User
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let LearningPeriod = 7d;
let EndLearningTime = now();
let BinTime = 1h;
let RunTime = 1h;
let MinThreshold = 3.0;

let GitLabFailedLogins is inlined into the numbered stages below.

Stages 1 to 4 define let GitLabFailedLogins as the rule's main pipeline source. Stages 5 to 11 use it.

Stage 1: source

GitLabApp

Stage 2: where

| where FailedLogin == 1

Stage 3: parse

| parse kind=regex Message with "Failed Login: username=" User "ip=" IpAddress

Stage 4: project

| project TimeGenerated, EventTime, Computer, User, HostName, HostIP, IpAddress

Stage 5: where

GitLabFailedLogins
| where EventTime between (ago(LearningPeriod) .. EndLearningTime)

Stage 6: summarize

| summarize FailedLoginsCountInBinTime = count() by User, bin(EventTime, BinTime)

Stage 7: summarize

| summarize AvgOfFailedLoginsInLearning = avg(FailedLoginsCountInBinTime), StdOfFailedLoginsInLearning = stdev(FailedLoginsCountInBinTime) by User

Stage 8: extend

| extend LearningThreshold = max_of(AvgOfFailedLoginsInLearning, MinThreshold)

Stage 9: join

| join kind=innerunique ( GitLabFailedLogins 
| summarize FailedLoginsCountInRunTime = count() by User, IpAddress, EventTime = bin(EventTime, BinTime) ) on User

Stage 10: where

where FailedLoginsCountInRunTime >= LearningThreshold

Stage 11: project

project EventTime, FailedLoginsCountInRunTime, IpAddress, LearningThreshold, User

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventTimeproject
FailedLoginsCountInRunTimeproject
IpAddressproject
LearningThresholdproject
Userproject

Hunt for accounts with leaked credentials

#
Group by
AccountObjectId
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This query searches for accounts where Exposure Management detected leaked credentials. This query is correlated with the IdentityInfo table, mainly because you can easily create a detection of this rule if you would like to.

Telemetry Coverage

Rule Body

IdentityInfo
| summarize arg_max(TimeGenerated, AccountUpn, AccountDisplayName, AccountDomain, CriticalityLevel, DistinguishedName) by AccountObjectId
| join kind=inner (
    ExposureGraphNodes
    // Get accounts with Leaked Credentials
    | where NodeProperties.rawData.hasAdLeakedCredentials == "true" or NodeProperties.rawData.hasLeakedCredentials == "true"
    // Get the AAD Object ID
    | mv-expand EntityIds
    | where EntityIds.type == "AadObjectId"
    | extend AccountObjectId = extract('objectid=(.*)', 1, tostring(EntityIds.id))
    | extend HasAdLeakedCredentials = tostring(NodeProperties.rawData.hasAdLeakedCredentials),
        HasLeakedCredentials = tostring(NodeProperties.rawData.hasLeakedCredentials)
    | distinct NodeLabel, AccountObjectId, HasAdLeakedCredentials, HasLeakedCredentials
) on AccountObjectId

Stages and Predicates

Stage 1: source

IdentityInfo

Stage 2: summarize

| summarize arg_max(TimeGenerated, AccountUpn, AccountDisplayName, AccountDomain, CriticalityLevel, DistinguishedName) by AccountObjectId

Stage 3: join

| join kind=inner (
    ExposureGraphNodes
    | where NodeProperties.rawData.hasAdLeakedCredentials == "true" or NodeProperties.rawData.hasLeakedCredentials == "true"
    | mv-expand EntityIds
    | where EntityIds.type == "AadObjectId"
    | extend AccountObjectId = extract('objectid=(.*)', 1, tostring(EntityIds.id))
    | extend HasAdLeakedCredentials = tostring(NodeProperties.rawData.hasAdLeakedCredentials),
        HasLeakedCredentials = tostring(NodeProperties.rawData.hasLeakedCredentials)
    | distinct NodeLabel, AccountObjectId, HasAdLeakedCredentials, HasLeakedCredentials
) on AccountObjectId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountObjectIdsummarize

Hunt for local AI Agent activity with Agent Info

#
Group by
$left.ExtractedObservabilityID, $left.SourceAgentId, $right.ObservabilityID, $right.SourceAIAgentId, AgentId
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

This query allows you to find the activity a local AI Agent performed via the CloudAppEvents table using the AgentsInfo and ExposureGraphNodes table.

References

Telemetry Coverage

Rule Body

// Fill in agent name or one of the Agent IDs you have found
let agent_name = "";
let some_agent_id = "";
AgentsInfo 
| where TimeGenerated > ago(1d)
| summarize arg_max(TimeGenerated, *) by AgentId
| where (isempty(some_agent_id) and Name =~ agent_name) or (isempty(agent_name) and * has some_agent_id)
// Take local AI Agents
| where Platform == "LocalAgents"
| distinct Name, Platform, SourceAgentId
// Join with Graph Nodes to get the ObservabilityID
| join kind=inner (
    ExposureGraphNodes
    | where NodeLabel == "ai-agent"
    | where parse_json(NodeProperties).rawData.aiAgentMetadata.platform == "LocalAgents"
    | extend SourceAIAgentId = extract("{\"type\":\"SourceAIAgentId\",\"id\":\"([^\"]+)\"}", 1, tostring(EntityIds))
    | extend A365RegistryAIAgentId = extract("{\"type\":\"A365RegistryAIAgentId\",\"id\":\"tenantid=([^;]+);titleid=([^\"]+)\"}", 2, tostring(EntityIds))
    | project SourceAIAgentId, A365RegistryAIAgentId
) on $left.SourceAgentId == $right.SourceAIAgentId
| extend ExtractedObservabilityID = iff(
    A365RegistryAIAgentId matches regex @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})",
    extract(@"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 1, A365RegistryAIAgentId),
    A365RegistryAIAgentId
)
| join kind=inner (
    CloudAppEvents
    | where ActionType in ("InvokeAgent","InferenceCall","ExecuteToolBySDK","ExecuteToolByGateway","ExecuteToolByMCPServer")
    // Extract the platformIDs and ObservabilityID
    | extend PlatformAgentId = tostring(parse_json(RawEventData)["PlatformAgentId"]), 
        PlatformTargetAgentId = tostring(parse_json(RawEventData)["PlatformTargetAgentId"])
    | extend PlatformId = iff(isempty(PlatformAgentId) and isnotempty(PlatformTargetAgentId), PlatformTargetAgentId, PlatformAgentId)
    | extend ObservabilityID = iff(
        PlatformId matches regex @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 
        extract(@"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 1, PlatformId),
        PlatformId
    )
) on $left.ExtractedObservabilityID == $right.ObservabilityID

Stages and Predicates

Parameters

let agent_name = "";
let some_agent_id = "";

Stage 1: source

AgentsInfo

Stage 2: where

| where TimeGenerated > ago(1d)

Stage 3: summarize

| summarize arg_max(TimeGenerated, *) by AgentId

Stage 4: where

| where (isempty(some_agent_id) and Name =~ agent_name) or (isempty(agent_name) and * has some_agent_id)

Stage 5: where

| where Platform == "LocalAgents"

Stage 6: distinct

| distinct Name, Platform, SourceAgentId

Stage 7: join

| join kind=inner (
    ExposureGraphNodes
    | where NodeLabel == "ai-agent"
    | where parse_json(NodeProperties).rawData.aiAgentMetadata.platform == "LocalAgents"
    | extend SourceAIAgentId = extract("{\"type\":\"SourceAIAgentId\",\"id\":\"([^\"]+)\"}", 1, tostring(EntityIds))
    | extend A365RegistryAIAgentId = extract("{\"type\":\"A365RegistryAIAgentId\",\"id\":\"tenantid=([^;]+);titleid=([^\"]+)\"}", 2, tostring(EntityIds))
    | project SourceAIAgentId, A365RegistryAIAgentId
) on $left.SourceAgentId == $right.SourceAIAgentId

Stage 8: extend

extend ExtractedObservabilityID
ExtractedObservabilityID =
ifA365RegistryAIAgentId matches regex @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})"extract(@"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 1, A365RegistryAIAgentId)
elseA365RegistryAIAgentId

Stage 9: join

| join kind=inner (
    CloudAppEvents
    | where ActionType in ("InvokeAgent","InferenceCall","ExecuteToolBySDK","ExecuteToolByGateway","ExecuteToolByMCPServer")
    | extend PlatformAgentId = tostring(parse_json(RawEventData)["PlatformAgentId"]), 
        PlatformTargetAgentId = tostring(parse_json(RawEventData)["PlatformTargetAgentId"])
    | extend PlatformId = iff(isempty(PlatformAgentId) and isnotempty(PlatformTargetAgentId), PlatformTargetAgentId, PlatformAgentId)
    | extend ObservabilityID = iff(
        PlatformId matches regex @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 
        extract(@"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})", 1, PlatformId),
        PlatformId
    )
) on $left.ExtractedObservabilityID == $right.ObservabilityID

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypein
  • ExecuteToolByGateway
  • ExecuteToolByMCPServer
  • ExecuteToolBySDK
  • InferenceCall
  • InvokeAgent
field:"ActionType" kind:in
NodeLabeleq
  • ai-agent
field:"NodeLabel" kind:eq value:"ai-agent"
NodeProperties.rawData.aiAgentMetadata.platformeq
  • LocalAgents transforms: kusto_accessor_expr:{"expression":"parse_json(NodeProperties).rawData.aiAgentMetadata.platform","field":"NodeProperties.rawData.aiAgentMetadata.platform"}, kusto_dynamic_path
field:"NodeProperties.rawData.aiAgentMetadata.platform" kind:eq value:"LocalAgents"
Platformeq
  • LocalAgents
field:"Platform" kind:eq value:"LocalAgents"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AgentIdsummarize
ExtractedObservabilityIDextend

Imperva - Critical severity event not blocked

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

'Detects when critical severity event was not blocked.'

MITRE ATT&CK Coverage

Rule Body

id: 4d365217-f96a-437c-9c57-53594fa261c3
name: Imperva - Critical severity event not blocked
description: |
  'Detects when critical severity event was not blocked.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ImpervaWAFCloudAPI
    dataTypes:
      - ImpervaWAFCloud
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  ImpervaWAFCloud
  | where EventSeverity =~ 'CRITICAL'
  | where DvcAction !startswith 'REQ_BLOCKED' or DvcAction !startswith 'REQ_BAD_'
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

ImpervaWAFCloud

Stage 2: where

| where EventSeverity =~ 'CRITICAL'

Stage 3: where

| where DvcAction !startswith 'REQ_BLOCKED' or DvcAction !startswith 'REQ_BAD_'

Stage 4: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPCustomEntityextend

Oracle - Multiple server errors from single IP

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

'Detects multiple server errors from one source in short timeframe'

MITRE ATT&CK Coverage

Rule Body

id: 268f4fde-5740-11ec-bf63-0242ac130002
name: Oracle - Multiple server errors from single IP
description: |
  'Detects multiple server errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    dataTypes:
      - OracleWebLogicServer_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - InitialAccess
relevantTechniques:
  - T1498
  - T1190
  - T1133
query: |
  let threshold = 100;
  OracleWebLogicServerEvent
  | where HttpStatusCode >= 500 and HttpStatusCode <= 599 
  | summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleServerErrors > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;

Stage 1: source

OracleWebLogicServerEvent

Stage 2: where

| where HttpStatusCode >= 500 and HttpStatusCode <= 599

Stage 3: summarize

| summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)

Stage 4: where

| where MultipleServerErrors > threshold

Stage 5: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MultipleServerErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

Pathlock TDnR - Authorization Check Value Changes (SU24)

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

Detects changes to authorization check values in the SU24 table in SAP, forwarded by Pathlock Threat Detection and Response. SU24 defines which authorization objects are checked for each transaction; unauthorized changes can effectively disable authorization checks and allow privilege escalation without modifying roles or profiles.

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation
Defense Impairment

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c62
name: Pathlock TDnR - Authorization Check Value Changes (SU24)
kind: Scheduled
description: >-
  Detects changes to authorization check values in the SU24 table in SAP, forwarded by Pathlock
  Threat Detection and Response. SU24 defines which authorization objects are checked for each
  transaction; unauthorized changes can effectively disable authorization checks and allow
  privilege escalation without modifying roles or profiles.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - PrivilegeEscalation
relevantTechniques:
  - T1562
  - T1548
query: |
  Pathlock_TDnR_CL
  | where DataSource == "SU24_CHANGELOG"
  | 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 == "SU24_CHANGELOG"

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

Ping Federate - Abnormal password resets for user

#
Status
available
Severity
high
Time window
1d
Group by
DstUserName
Source
github.com/Azure/Azure-Sentinel

'Detects multiple password reset for user.'

MITRE ATT&CK Coverage

Rule Body

id: 6145efdc-4724-42a6-9756-5bd1ba33982e
name: Ping Federate - Abnormal password resets for user
description: |
  'Detects multiple password reset for user.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1078
  - T1098
  - T1134
query: |
  let threshold = 10;
  PingFederateEvent
  | where EventType =~ 'PWD_RESET'
  | summarize count() by DstUserName
  | where count_ > threshold
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 10;

Stage 1: source

PingFederateEvent

Stage 2: where

| where EventType =~ 'PWD_RESET'

Stage 3: summarize

| summarize count() by DstUserName

Stage 4: where

| where count_ > threshold

Stage 5: extend

| extend AccountCustomEntity = DstUserName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNamesummarize
AccountCustomEntityextend

SailPointIdentityNowEventTypeTechnicalName

#
Status
available
Severity
high
Time window
14d
Source
github.com/Azure/Azure-Sentinel

'Created to detect new threat events from the data in SailPointIDN_Events.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 2151e8ea-4838-4c74-be12-4d6a950dde7a
name: SailPointIdentityNowEventTypeTechnicalName
description: |
  'Created to detect new threat events from the data in SailPointIDN_Events.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SailPointIdentityNow
    dataTypes:
      - SailPointIDN_Events
  - connectorId: SailPointIdentityNowConnector
    dataTypes:
      - SailPointIDN_Events
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1133
query: |
  declare query_parameters(lbperiod:timespan = 14d, technicalName:string = "ENTITLEMENT_ADD_FAILED", type:string = 'ACCESS_ITEM');
    SailPointIDN_Events
    | where TimeGenerated > ago(lbperiod)
    | where EventType == type
    | where TechnicalName == technicalName
    | where Status == "FAILED"
    | sort by Created
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: TechnicalName
version: 1.1.0
kind: Scheduled

Stages and Predicates

Stage 1: source

declare query_parameters(lbperiod:timespan = 14d, technicalName:string = "ENTITLEMENT_ADD_FAILED", type:string = 'ACCESS_ITEM');
  SailPointIDN_Events

Stage 2: where

| where TimeGenerated > ago(lbperiod)

Stage 3: where

| where EventType == type

Stage 4: where

| where TechnicalName == technicalName

Stage 5: where

| where Status == "FAILED"

Stage 6: sort

| sort by Created

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Statuseq
  • FAILED
field:"Status" kind:eq value:"FAILED"

Sdelete deployed via GPO and run recursively

#
Status
available
Severity
medium
Time window
1d
Group by
$left.NewProcessId, $right.ProcessId, Computer, timekey
Source
github.com/Azure/Azure-Sentinel

This query looks for the Sdelete process being run recursively after being deployed to a host via GPO. Attackers could use this technique to deploy Sdelete to multiple host and delete data on them.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Telemetry Coverage

Rule Body

id: d9f28fdf-abc8-4f1a-a7e7-1aaec87a2fc5
name: Sdelete deployed via GPO and run recursively
description: |
  'This query looks for the Sdelete process being run recursively after being deployed to a host via GPO. Attackers could use this technique to deploy Sdelete to multiple host and delete data on them.'
severity: Medium
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Impact
relevantTechniques:
  - T1485
tags:
  -
query: |
  SecurityEvent
    | where EventID == 4688
    | where Process =~ "svchost.exe"
    | where CommandLine has "-k GPSvcGroup" or CommandLine has "-s gpsvc"
    | extend timekey = bin(TimeGenerated, 1m)
    | project timekey, NewProcessId, Computer
    | join kind=inner (SecurityEvent
    | where EventID == 4688
    | where Process =~ "sdelete.exe" or CommandLine has "sdelete"
    | where ParentProcessName endswith "svchost.exe"
    | where CommandLine has_all ("-s", "-r")
    | extend newProcess = Process
    | extend timekey = bin(TimeGenerated, 1m)
    ) on $left.NewProcessId == $right.ProcessId, timekey, Computer
    | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
    | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
    | extend AccountName = tostring(split(TargetAccount, @'\')[1]), AccountNTDomain = tostring(split(TargetAccount, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: TargetAccount
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 4688

Stage 3: where

| where Process =~ "svchost.exe"

Stage 4: where

| where CommandLine has "-k GPSvcGroup" or CommandLine has "-s gpsvc"

Stage 5: extend

| extend timekey = bin(TimeGenerated, 1m)

Stage 6: project

| project timekey, NewProcessId, Computer

Stage 7: join

| join kind=inner (SecurityEvent
  | where EventID == 4688
  | where Process =~ "sdelete.exe" or CommandLine has "sdelete"
  | where ParentProcessName endswith "svchost.exe"
  | where CommandLine has_all ("-s", "-r")
  | extend newProcess = Process
  | extend timekey = bin(TimeGenerated, 1m)
  ) on $left.NewProcessId == $right.ProcessId, timekey, Computer

Stage 8: extend (3 consecutive steps)

extend AccountNTDomain, AccountName, DomainIndex, HostName, HostNameDomain

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinematch
  • -k GPSvcGroup transforms: term corpus 2 (kusto 2)
  • -r transforms: term
  • -s transforms: term
  • -s gpsvc transforms: term corpus 2 (kusto 2)
  • sdelete transforms: term
field:"CommandLine" kind:match
EventIDeq
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq value:"4688"
ParentProcessNameends_with
  • svchost.exe
field:"ParentProcessName" kind:ends_with value:"svchost.exe"
Processeq
  • sdelete.exe
  • svchost.exe
field:"Process" kind:eq

Output Fields

These fields are emitted when the rule matches.

FieldSource
Computerproject
NewProcessIdproject
timekeyproject
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend

Service Provider Updated

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

Detects when service provider settings are updated in Veeam Backup & Replication.

Rule Body

id: 35fb4771-9b8f-47f7-bd5b-3085d584f7ce
name: Service Provider Updated
description: Detects when service provider settings are updated in Veeam Backup &
  Replication.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 27500\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    UserName = user,\n  MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 27500

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    UserName = user,
  MessageDetails = Description,
    Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
UserNameproject

Sonrai Ticket Updated

#

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
5m
Source
github.com/Azure/Azure-Sentinel

'Checks if Sonrai tickets have been updated. It uses the action type to check if a ticket has been updated'

MITRE ATT&CK Coverage

Rule Body

id: af9b8eb1-a8ef-40aa-92a4-1fc73a1479c7
name: Sonrai Ticket Updated
description: |
  'Checks if Sonrai tickets have been updated. 
  It uses the action type to check if a ticket has been updated'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SonraiDataConnector
    dataTypes:
      - Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - CommandAndControl
  - CredentialAccess
  - DefenseEvasion
  - Discovery
  - Execution
  - Exfiltration
  - Impact
  - InitialAccess
  - LateralMovement
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1566
  - T1059
  - T1547
  - T1548
  - T1562
  - T1003
  - T1087
  - T1021
  - T1119
  - T1071
  - T1041
  - T1499
query: |
  Sonrai_Tickets_CL
  | where action_d == 5
entityMappings: 
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: digest_criticalResourceName_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  ticketName: digest_title_s
  ticketStatus: digest_status_s
  ticketSeverity: digest_severityCategory_s
  resourceLabel: digest_resourceLabel_s
  resourceType: digest_resourceType_s
  criticalResource: digest_criticalResourceName_s
  ticketOrg: digest_org_s
alertDetailsOverride:
  alertSeverityColumnName: digest_severityCategory_s
  alertDisplayNameFormat: Updated - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
  alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

Sonrai_Tickets_CL

Stage 2: where

| where action_d == 5

Indicators

These rows show field, operator, and value matches.

Tenant Replica Started

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

Detects when a tenant replica starts.

Rule Body

id: c4d891cd-4b22-419c-8f40-7603eb1cc3a3
name: Tenant Replica Started
description: Detects when a tenant replica starts.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 26800\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    UserName = user,\n  MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 26800

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    UserName = user,
  MessageDetails = Description,
    Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
UserNameproject

Tomcat - Multiple client errors from single IP address

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

'Detects multiple client errors from one source in short timeframe'

MITRE ATT&CK Coverage

Rule Body

id: 4fa66058-4870-11ec-81d3-0242ac130003
name: Tomcat - Multiple client errors from single IP address
description: |
  'Detects multiple client errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let threshold = 100;
  TomcatEvent
  | where HttpStatusCode >= 400 and HttpStatusCode <= 499 
  | summarize MultipleClientErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleClientErrors > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;

Stage 1: source

TomcatEvent

Stage 2: where

| where HttpStatusCode >= 400 and HttpStatusCode <= 499

Stage 3: summarize

| summarize MultipleClientErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)

Stage 4: where

| where MultipleClientErrors > threshold

Stage 5: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MultipleClientErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

Valimail Enforce - DMARC Policy Weakened to None

#
Status
available
Severity
high
Time window
1h
Group by
EventCategory, Subject, User
Source
github.com/Azure/Azure-Sentinel

This query searches for DMARC policies changed to 'none', which disables enforcement and leaves the domain vulnerable to spoofing and phishing attacks.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Defense Impairment

Rule Body

id: 44ec1fa4-a502-41ae-879a-3aad3557edce
name: Valimail Enforce - DMARC Policy Weakened to None
description: |
  This query searches for DMARC policies changed to 'none', which disables enforcement
  and leaves the domain vulnerable to spoofing and phishing attacks.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ValimailEnforce
    dataTypes:
      - ValimailEnforceEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - InitialAccess
relevantTechniques:
  - T1566
  - T1562
query: |
  ValimailEnforceEvents_CL
  | where EventSeverity == "High"
  | where EventType == "dmarc_policy_set_to_none"
  | summarize
      EventCount = count(),
      FirstSeen = min(PerformedAt),
      LastSeen = max(PerformedAt),
      Changes = make_set(EventChange)
    by Subject, User, EventCategory
  | extend
      AccountName = tostring(split(User, "@")[0]),
      AccountDomain = tostring(split(User, "@")[1]),
      DomainName = Subject
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountDomain
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: DomainName
alertDetailsOverride:
  alertDisplayNameFormat: "DMARC policy set to NONE on domain {{Subject}} by {{User}}"
  alertDescriptionFormat: |
    The DMARC policy for domain '{{Subject}}' was set to 'none' by '{{User}}',
    disabling email authentication enforcement. This may expose the domain to spoofing.
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1d
    matchingMethod: Selected
    groupByEntities:
      - DNS
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

ValimailEnforceEvents_CL

Stage 2: where

| where EventSeverity == "High"

Stage 3: where

| where EventType == "dmarc_policy_set_to_none"

Stage 4: summarize

| summarize
    EventCount = count(),
    FirstSeen = min(PerformedAt),
    LastSeen = max(PerformedAt),
    Changes = make_set(EventChange)
  by Subject, User, EventCategory

Stage 5: extend

| extend
    AccountName = tostring(split(User, "@")[0]),
    AccountDomain = tostring(split(User, "@")[1]),
    DomainName = Subject

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Changessummarize
EventCategorysummarize
EventCountsummarize
FirstSeensummarize
LastSeensummarize
Subjectsummarize
Usersummarize
AccountDomainextend
AccountNameextend
DomainNameextend

ZeroFox Alerts - Medium Severity Alerts

#

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
5m
Source
github.com/Azure/Azure-Sentinel

'Detects medium severity alerts from ZeroFox'

MITRE ATT&CK Coverage

TacticTechniques
Resource Development
Initial Access

Rule Body

id: a6496de5-911b-4199-b7db-d34ac9d70df3
name: ZeroFox Alerts - Medium Severity Alerts
description: |
  'Detects medium severity alerts from ZeroFox'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: ZeroFox_Alert_Polling
    dataTypes:
      - ZeroFoxAlertPoller_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - ResourceDevelopment
  - InitialAccess
relevantTechniques:
  - T1583
  - T1586
  - T1566
query: |
  ZeroFoxAlertPoller_CL
  | where Severity in (4)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: entity_name_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
kind: Scheduled
version: 1.0.0

Stages and Predicates

Stage 1: source

ZeroFoxAlertPoller_CL

Stage 2: where

| where Severity in (4)

Indicators

These rows show field, operator, and value matches.

Back to all Kusto rules