Adversaries may be using automated tools or scripts to perform excessive sign-in attempts during off-hours to evade detection. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential credential compromise or unauthorized access attempts.
KQL Query
let admins = (IdentityInfo
| where AssignedRoles contains "Admin"
| summarize by tolower(AccountUPN));
let ts_data = (SigninLogs
| where TimeGenerated > ago(14d)
| extend AccountUPN = tolower(UserPrincipalName)
| where AccountUPN in (admins)
| make-series count() on TimeGenerated step 1h by UserPrincipalName
| extend series_decompose(count_)
| extend NoLogons = count_);
let TimeSeriesAlerts=ts_data
| extend (anomalies, score, baseline) = series_decompose_anomalies(count_, 2.5, -1, 'linefit',0, 'ctukey', 3)
| mv-expand NoLogons to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double),score to typeof(double), baseline to typeof(long)
| where anomalies > 0
| where NoLogons > 15
| project UserPrincipalName, TimeGenerated, NoLogons, baseline, anomalies, score;
TimeSeriesAlerts
| join kind=inner (
SigninLogs
| summarize ResultTypeCount=count(),ResultTypes=make_set(ResultType), Locations=make_set(Location), Apps=make_set(AppDisplayName), Ips=make_set( IPAddress) by UserPrincipalName, bin(TimeGenerated, 1h)
) on UserPrincipalName, TimeGenerated
| summarize AnomolyTimes = make_set(TimeGenerated), Ips = make_set(Ips), Apps = make_set(Apps), sum(anomalies), Locations=make_set(Locations) by UserPrincipalName
| sort by sum_anomalies desc
id: 8ed5b8f1-a43a-49dc-847c-e44d7a590c17
name: Anomolous Sign Ins Based on Time
description: |
'Identifies anomolies in signin events based on the volume of signin events over time. Use this to identify suspicious authentication patterns such as spikes in activity or out of hours events.
Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#things-to-monitor'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: BehaviorAnalytics
dataTypes:
- IdentityInfo
tactics:
- InitialAccess
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
let admins = (IdentityInfo
| where AssignedRoles contains "Admin"
| summarize by tolower(AccountUPN));
let ts_data = (SigninLogs
| where TimeGenerated > ago(14d)
| extend AccountUPN = tolower(UserPrincipalName)
| where AccountUPN in (admins)
| make-series count() on TimeGenerated step 1h by UserPrincipalName
| extend series_decompose(count_)
| extend NoLogons = count_);
let TimeSeriesAlerts=ts_data
| extend (anomalies, score, baseline) = series_decompose_anomalies(count_, 2.5, -1, 'linefit',0, 'ctukey', 3)
| mv-expand NoLogons to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double),score to typeof(double), baseline to typeof(long)
| where anomalies > 0
| where NoLogons > 15
| project UserPrincipalName, TimeGenerated, NoLogons, baseline, anomalies, score;
TimeSeriesAlerts
| join kind=inner (
SigninLogs
| summarize ResultTypeCount=count(),ResultTypes=make_set(ResultType), Locations=make_set(Location), Apps=make_set(AppDisplayName), Ips=make_set( IPAddress) by UserPrincipalName, bin(TimeGenerated, 1h)
) on UserPrincipalName, TimeGenerated
| summarize AnomolyTimes = make_set(TimeGenerated), Ips = make_set(Ips), Apps = make_set(Apps), sum(anomalies), Locations=make_set(Locations) by UserPrincipalName
| sort by sum_anomalies desc
version: 1.0.1
| Sentinel Table | Notes |
|---|---|
IdentityInfo | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled System Maintenance or Patching
Description: Automated maintenance tasks or patching jobs run during off-hours, causing a spike in sign-in events.
Filter/Exclusion: src_user = "system_user" OR src_user = "admin_user" OR src_ip IN (list of known maintenance IPs)
Scenario: Backup or Data Sync Jobs
Description: Scheduled backups or data synchronization processes that authenticate to the system during non-business hours.
Filter/Exclusion: src_user = "backup_user" OR src_user = "sync_user" OR event_id = "backup_event_id"
Scenario: Admin Task Execution (e.g., via PowerShell or CLI)
Description: An administrator performing routine tasks (e.g., user management, configuration changes) using command-line tools or scripts.
Filter/Exclusion: src_user = "admin_user" OR src_ip IN (list of admin IPs) OR event_id = "command_line_execution"
Scenario: Log Collection or Monitoring Tools
Description: Log aggregation tools (e.g., Splunk, ELK Stack, or Azure Monitor) authenticating to the system to collect logs.
Filter/Exclusion: src_user = "log_collector_user" OR src_ip IN (list of log collection IPs) OR event_id = "log_collection"
Scenario: User Login During Non-Working Hours (Legitimate Shifts)
Description: Employees working night shifts or remote workers logging in during non-business hours.
Filter/Exclusion: user_department = "night_shift" OR user_location = "remote" OR user_timezone = "UTC+8"