Detection rules › Kusto
GSA - Detect Connections Outside Operational Hours
This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1078 Valid Accounts, T1133 External Remote Services |
Rule body kusto
id: 4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa
name: GSA - Detect Connections Outside Operational Hours
description: |
This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- NetworkAccessTrafficLogs
queryFrequency: 1h
queryPeriod: 24h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
- T1133
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let operational_start_hour = 8; // Start of operational hours (8 AM)
let operational_end_hour = 18; // End of operational hours (6 PM)
NetworkAccessTraffic
| where TimeGenerated between (starttime .. endtime)
| extend HourOfDay = datetime_part('hour', TimeGenerated)
| where HourOfDay < operational_start_hour or HourOfDay >= operational_end_hour
| project TimeGenerated, UserPrincipalName, SourceIp, DestinationIp, DestinationPort, Action, DeviceId, DeviceOperatingSystem, ConnectionId
| extend IPCustomEntity = SourceIp, AccountCustomEntity = UserPrincipalName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let operational_start_hour = 8;
let operational_end_hour = 18;
Stage 1: source
NetworkAccessTraffic
Stage 2: where
| where TimeGenerated between (starttime .. endtime)
Stage 3: extend
| extend HourOfDay = datetime_part('hour', TimeGenerated)
Stage 4: where
| where HourOfDay < operational_start_hour or HourOfDay >= operational_end_hour
Stage 5: project
| project TimeGenerated, UserPrincipalName, SourceIp, DestinationIp, DestinationPort, Action, DeviceId, DeviceOperatingSystem, ConnectionId
Stage 6: extend
| extend IPCustomEntity = SourceIp, AccountCustomEntity = UserPrincipalName
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
HourOfDay | ge |
|
HourOfDay | lt |
|
TimeGenerated | ge |
|
TimeGenerated | le |
|
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.
| Field | Source |
|---|---|
Action | project |
ConnectionId | project |
DestinationIp | project |
DestinationPort | project |
DeviceId | project |
DeviceOperatingSystem | project |
SourceIp | project |
TimeGenerated | project |
UserPrincipalName | project |
AccountCustomEntity | extend |
IPCustomEntity | extend |