Security Alert Alert Generation Anomaly
Query
let query_frequency = 1h;
let query_period = 7d;
let scan_step = 5m;
let consecutive_failures_threshold = 2;
let _SecurityAlertTimeSeries = (start_time: datetime, end_time: datetime) {
let _Auxiliar = toscalar(
SecurityAlert
| where TimeGenerated between (start_time .. end_time)
| make-series Count = count() default=0 on TimeGenerated step scan_step
| extend
TimeGenerated = array_slice(TimeGenerated, 0, toint(-(consecutive_failures_threshold * query_frequency / scan_step))),
Count = array_slice(Count, 0, toint(-(consecutive_failures_threshold * query_frequency / scan_step)))
| extend series_periods_detect(
Count,
0.0,
toint(24h / scan_step),
1)
| summarize Period = take_any(toint(series_periods_detect_Count_periods[0]))
);
let _PeriodStep = scan_step * abs(coalesce(_Auxiliar, 1));
SecurityAlert
| where TimeGenerated between (start_time .. end_time)
// _PeriodStep cannot be used with make-series, so summarize has to be used instead
// | make-series Count = count() default=0 on TimeGenerated step _PeriodStep
// summarize does not generate zero values for count(), baseline noise has to be added, it will be "deleted" afterwards
| union (range TimeGenerated from start_time to end_time step _PeriodStep)
| summarize Count = count() by bin_at(TimeGenerated, _PeriodStep, end_time)
| extend Count = Count - 1
| summarize TimeGenerated = make_list(TimeGenerated), Count = make_list(Count)
| extend
TimeGenerated = array_slice(TimeGenerated, 0, -2),
Count = array_slice(Count, 0, -2)
| extend series_decompose_anomalies(Count)
| where array_sum(array_slice(series_decompose_anomalies_Count_ad_flag, -(consecutive_failures_threshold), -1)) == (-1 * consecutive_failures_threshold)
};
_SecurityAlertTimeSeries(ago(query_period), now())
// Uncomment the following line if you want only one alert (during the first query_frequency of the anomaly)
| where not(toscalar(_SecurityAlertTimeSeries(ago(query_period), ago(query_frequency)) | count) > 0)
| render timechartExplanation
This KQL query is designed to detect anomalies in security alerts over a specified period. Here's a simplified explanation:
-
Parameters Setup:
query_frequency: The frequency at which the query is run (1 hour).query_period: The total time period over which the data is analyzed (7 days).scan_step: The granularity of the time intervals for analysis (5 minutes).consecutive_failures_threshold: The number of consecutive anomalies needed to trigger an alert (2).
-
Data Preparation:
- The query defines a function
_SecurityAlertTimeSeriesthat takes a start and end time as inputs. - It calculates the number of security alerts generated within the specified time frame using a time series analysis.
- The data is sliced to remove the last few intervals based on the
consecutive_failures_threshold.
- The query defines a function
-
Anomaly Detection:
- The query uses
series_periods_detectto identify periodic patterns in the alert counts. - It calculates a step size (
_PeriodStep) based on the detected period and uses it to bin the data. - A baseline noise is added to ensure that zero values are handled correctly.
- The query uses
-
Anomaly Analysis:
- The query uses
series_decompose_anomaliesto detect anomalies in the alert counts. - It checks for consecutive anomalies (as defined by
consecutive_failures_threshold) and flags them.
- The query uses
-
Output:
- The function
_SecurityAlertTimeSeriesis called to analyze data from the past 7 days up to the current time. - Optionally, a line can be uncommented to filter out alerts that have already been detected in the last hour.
- The results are rendered as a time chart for visualization.
- The function
In summary, this query identifies periods with unusual spikes in security alerts over the past week, highlighting potential security incidents that may require further investigation.
Details

Jose Sebastián Canós
Released: October 31, 2024
Tables
SecurityAlert
Keywords
SecurityAlertTimeGeneratedCountPeriodAnomalies
Operators
lettoscalarwherebetweenmake-seriescountdefaultonstepextendarray_slicetointseries_periods_detectsummarizetake_anycoalesceabsunionrangefromtobin_atbymake_listseries_decompose_anomaliesarray_sumagonownotrender