Detection rules › Kusto

Unusual Anomaly

Severity
medium
Time window
4d
Source
github.com/Azure/Azure-Sentinel

'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'

Rule body kusto

id: d0255b5f-2a3c-4112-8744-e6757af3283a
name: Unusual Anomaly
description: |
  'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 4d
triggerOperator: gt
triggerThreshold: 0
tactics: []
techniques: []
query: |
  // You can leave out Anomalies that are already monitored through other Analytics Rules
  //let _MonitoredRules = dynamic(["TestAlertName"]);
  let query_frequency = 1h;
  let query_lookback = 3d;
  Anomalies
  | where TimeGenerated > ago(query_frequency)
  //| where not(RuleName has_any (_MonitoredRules))
  | join kind = leftanti (
      Anomalies
      | where TimeGenerated between (ago(query_frequency + query_lookback)..ago(query_frequency))
      | distinct RuleName
  ) on RuleName
  | extend Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: Unusual Anomaly - {{RuleName}}
  alertTacticsColumnName: Tactics
  alertDynamicProperties:
    - alertProperty: Techniques
      value: Techniques
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let query_frequency = 1h;
let query_lookback = 3d;

Stage 1: source

Anomalies

Stage 2: where

| where TimeGenerated > ago(query_frequency)

Stage 3: join (negated)

| join kind = leftanti (
    Anomalies
    | where TimeGenerated between (ago(query_frequency + query_lookback)..ago(query_frequency))
    | distinct RuleName
) on RuleName

Stage 4: extend

| extend Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
Nameextend
UPNSuffixextend