Process Execution Frequency Anomaly
Id | 2c55fe7a-b06f-4029-a5b9-c54a2320d7b8 |
Rulename | Process Execution Frequency Anomaly |
Description | This detection identifies anomalous spike in frequency of executions of sensitive processes which are often leveraged as attack vectors. The query leverages KQL’s built-in anomaly detection algorithms to find large deviations from baseline patterns. Sudden increases in execution frequency of sensitive processes should be further investigated for malicious activity. Tune the values from 1.5 to 3 in series_decompose_anomalies for further outliers or based on custom threshold values for score. |
Severity | Medium |
Tactics | Execution |
Techniques | T1059 |
Required data connectors | SecurityEvents WindowsSecurityEvents |
Kind | Scheduled |
Query frequency | 1d |
Query period | 14d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/TimeSeriesAnomaly-ProcessExecutions.yaml |
Version | 1.0.6 |
Arm template | 2c55fe7a-b06f-4029-a5b9-c54a2320d7b8.json |
let starttime = 14d;
let endtime = 1d;
let timeframe = 1h;
let TotalEventsThreshold = 5;
// Configure the list with sensitive process names
let ExeList = dynamic(["powershell.exe","cmd.exe","wmic.exe","psexec.exe","cacls.exe","rundll32.exe"]);
let TimeSeriesData =
SecurityEvent
| where EventID == 4688 | extend Process = tolower(Process)
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| where Process in~ (ExeList)
| project TimeGenerated, Computer, AccountType, Account, Process
| make-series Total=count() on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by Process;
let TimeSeriesAlerts = materialize(TimeSeriesData
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 1.5, -1, 'linefit')
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
| where anomalies > 0
| project Process, TimeGenerated, Total, baseline, anomalies, score
| where Total > TotalEventsThreshold);
let AnomalyHours = materialize(TimeSeriesAlerts | where TimeGenerated > ago(2d) | project TimeGenerated);
TimeSeriesAlerts
| where TimeGenerated > ago(2d)
| join (
SecurityEvent
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| extend DateHour = bin(TimeGenerated, 1h) // create a new column and round to hour
| where DateHour in ((AnomalyHours)) //filter the dataset to only selected anomaly hours
| where EventID == 4688 | extend Process = tolower(Process)
| summarize CommandlineCount = count() by bin(TimeGenerated, 1h), Process, CommandLine, Computer, Account
) on Process, TimeGenerated
| project AnomalyHour = TimeGenerated, Computer, Account, Process, CommandLine, CommandlineCount, Total, baseline, anomalies, score
| extend timestamp = AnomalyHour, NTDomain = split(Account, '\\', 0)[0], Name = split(Account, '\\', 1)[0], HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
relevantTechniques:
- T1059
name: Process Execution Frequency Anomaly
requiredDataConnectors:
- dataTypes:
- SecurityEvent
connectorId: SecurityEvents
- dataTypes:
- SecurityEvent
connectorId: WindowsSecurityEvents
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: Account
- identifier: Name
columnName: Name
- identifier: NTDomain
columnName: NTDomain
entityType: Account
- fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
entityType: Host
triggerThreshold: 0
id: 2c55fe7a-b06f-4029-a5b9-c54a2320d7b8
tactics:
- Execution
version: 1.0.6
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/TimeSeriesAnomaly-ProcessExecutions.yaml
queryPeriod: 14d
kind: Scheduled
queryFrequency: 1d
severity: Medium
status: Available
description: |
'This detection identifies anomalous spike in frequency of executions of sensitive processes which are often leveraged as attack vectors.
The query leverages KQL's built-in anomaly detection algorithms to find large deviations from baseline patterns.
Sudden increases in execution frequency of sensitive processes should be further investigated for malicious activity.
Tune the values from 1.5 to 3 in series_decompose_anomalies for further outliers or based on custom threshold values for score.'
query: |
let starttime = 14d;
let endtime = 1d;
let timeframe = 1h;
let TotalEventsThreshold = 5;
// Configure the list with sensitive process names
let ExeList = dynamic(["powershell.exe","cmd.exe","wmic.exe","psexec.exe","cacls.exe","rundll32.exe"]);
let TimeSeriesData =
SecurityEvent
| where EventID == 4688 | extend Process = tolower(Process)
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| where Process in~ (ExeList)
| project TimeGenerated, Computer, AccountType, Account, Process
| make-series Total=count() on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by Process;
let TimeSeriesAlerts = materialize(TimeSeriesData
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 1.5, -1, 'linefit')
| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)
| where anomalies > 0
| project Process, TimeGenerated, Total, baseline, anomalies, score
| where Total > TotalEventsThreshold);
let AnomalyHours = materialize(TimeSeriesAlerts | where TimeGenerated > ago(2d) | project TimeGenerated);
TimeSeriesAlerts
| where TimeGenerated > ago(2d)
| join (
SecurityEvent
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| extend DateHour = bin(TimeGenerated, 1h) // create a new column and round to hour
| where DateHour in ((AnomalyHours)) //filter the dataset to only selected anomaly hours
| where EventID == 4688 | extend Process = tolower(Process)
| summarize CommandlineCount = count() by bin(TimeGenerated, 1h), Process, CommandLine, Computer, Account
) on Process, TimeGenerated
| project AnomalyHour = TimeGenerated, Computer, Account, Process, CommandLine, CommandlineCount, Total, baseline, anomalies, score
| extend timestamp = AnomalyHour, NTDomain = split(Account, '\\', 0)[0], Name = split(Account, '\\', 1)[0], HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
triggerOperator: gt
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/2c55fe7a-b06f-4029-a5b9-c54a2320d7b8')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/2c55fe7a-b06f-4029-a5b9-c54a2320d7b8')]",
"properties": {
"alertRuleTemplateName": "2c55fe7a-b06f-4029-a5b9-c54a2320d7b8",
"customDetails": null,
"description": "'This detection identifies anomalous spike in frequency of executions of sensitive processes which are often leveraged as attack vectors.\nThe query leverages KQL's built-in anomaly detection algorithms to find large deviations from baseline patterns.\nSudden increases in execution frequency of sensitive processes should be further investigated for malicious activity.\nTune the values from 1.5 to 3 in series_decompose_anomalies for further outliers or based on custom threshold values for score.'\n",
"displayName": "Process Execution Frequency Anomaly",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "Account",
"identifier": "FullName"
},
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "NTDomain",
"identifier": "NTDomain"
}
]
},
{
"entityType": "Host",
"fieldMappings": [
{
"columnName": "Computer",
"identifier": "FullName"
},
{
"columnName": "HostName",
"identifier": "HostName"
},
{
"columnName": "DnsDomain",
"identifier": "DnsDomain"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Windows Security Events/Analytic Rules/TimeSeriesAnomaly-ProcessExecutions.yaml",
"query": "let starttime = 14d;\nlet endtime = 1d;\nlet timeframe = 1h;\nlet TotalEventsThreshold = 5;\n// Configure the list with sensitive process names \nlet ExeList = dynamic([\"powershell.exe\",\"cmd.exe\",\"wmic.exe\",\"psexec.exe\",\"cacls.exe\",\"rundll32.exe\"]);\nlet TimeSeriesData =\nSecurityEvent\n| where EventID == 4688 | extend Process = tolower(Process)\n| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))\n| where Process in~ (ExeList)\n| project TimeGenerated, Computer, AccountType, Account, Process\n| make-series Total=count() on TimeGenerated from startofday(ago(starttime)) to startofday(ago(endtime)) step timeframe by Process;\nlet TimeSeriesAlerts = materialize(TimeSeriesData\n| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 1.5, -1, 'linefit')\n| mv-expand Total to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double), score to typeof(double), baseline to typeof(long)\n| where anomalies > 0\n| project Process, TimeGenerated, Total, baseline, anomalies, score\n| where Total > TotalEventsThreshold);\nlet AnomalyHours = materialize(TimeSeriesAlerts | where TimeGenerated > ago(2d) | project TimeGenerated);\nTimeSeriesAlerts\n| where TimeGenerated > ago(2d)\n| join (\nSecurityEvent\n| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))\n| extend DateHour = bin(TimeGenerated, 1h) // create a new column and round to hour\n| where DateHour in ((AnomalyHours)) //filter the dataset to only selected anomaly hours\n| where EventID == 4688 | extend Process = tolower(Process)\n| summarize CommandlineCount = count() by bin(TimeGenerated, 1h), Process, CommandLine, Computer, Account\n) on Process, TimeGenerated\n| project AnomalyHour = TimeGenerated, Computer, Account, Process, CommandLine, CommandlineCount, Total, baseline, anomalies, score\n| extend timestamp = AnomalyHour, NTDomain = split(Account, '\\\\', 0)[0], Name = split(Account, '\\\\', 1)[0], HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))\n",
"queryFrequency": "P1D",
"queryPeriod": "P14D",
"severity": "Medium",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Execution"
],
"techniques": [
"T1059"
],
"templateVersion": "1.0.6",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}