Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

StealthTalk - After hours work

Back
Ide3a8b2f1-5c7d-4d89-9b6e-0f1a2c3d4e5f
RulenameStealthTalk - After hours work
DescriptionIdentifies systematic off-hours activity for a single StealthTalk user - repeated authentications

outside the user’s configured working hours OR on weekends, observed across at least two

distinct calendar days within a 48-hour window. The pattern is a common indicator of credential

misuse, insider threat, or compromise of the account by an attacker operating in a different

timezone.



An “off-hours event” is one where IsWeekend=true OR DeviationMinutes >= 180 (i.e. >= 3 hours

after the configured working-hours end). Three or more such events on at least two distinct

days are required for an incident to fire - a single late-evening login is not enough.
SeverityLow
TacticsInitialAccess
DefenseEvasion
Persistence
TechniquesT1078
Required data connectorsStealthTalkAnomalousAuth
KindScheduled
Query frequency1h
Query period2d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/StealthTalk/Analytic Rules/AfterHoursWork.yaml
Version1.0.0
Arm templatee3a8b2f1-5c7d-4d89-9b6e-0f1a2c3d4e5f.json
Deploy To Azure
let LookbackPeriod    = 48h;
let MinAttempts       = 3;
let MinDistinctDays   = 2;
let OffHoursThreshold = 180;
StealthTalkAnomalousAuth_CL
| where TimeGenerated >= ago(LookbackPeriod)
| where EventType == "OffHoursLogin"
| where IsWeekend == true or DeviationMinutes >= OffHoursThreshold
| summarize
    AttemptCount   = count(),
    DistinctDays   = dcount(startofday(TimeGenerated)),
    FirstSeen      = min(TimeGenerated),
    LastSeen       = max(TimeGenerated),
    DeviceIds      = make_set(DeviceId),
    MaxDeviation   = max(DeviationMinutes),
    WeekendsCount  = countif(IsWeekend == true),
    AppVersions    = make_set(AppVersion)
  by UserId
| where AttemptCount >= MinAttempts and DistinctDays >= MinDistinctDays
| extend
    AlertName    = "AfterHoursWork",
    AlertDetails = strcat(
        "User ", UserId,
        " performed ", AttemptCount, " off-hours logins",
        " across ", DistinctDays, " distinct days.",
        " Max deviation from working hours: ", MaxDeviation, " min.",
        " Weekend logins: ", WeekendsCount, "."
    )
| project
    TimeGenerated = LastSeen,
    UserId, AttemptCount, DistinctDays, MaxDeviation,
    WeekendsCount, DeviceIds, AppVersions, FirstSeen, LastSeen,
    AlertName, AlertDetails
version: 1.0.0
severity: Low
query: |
  let LookbackPeriod    = 48h;
  let MinAttempts       = 3;
  let MinDistinctDays   = 2;
  let OffHoursThreshold = 180;
  StealthTalkAnomalousAuth_CL
  | where TimeGenerated >= ago(LookbackPeriod)
  | where EventType == "OffHoursLogin"
  | where IsWeekend == true or DeviationMinutes >= OffHoursThreshold
  | summarize
      AttemptCount   = count(),
      DistinctDays   = dcount(startofday(TimeGenerated)),
      FirstSeen      = min(TimeGenerated),
      LastSeen       = max(TimeGenerated),
      DeviceIds      = make_set(DeviceId),
      MaxDeviation   = max(DeviationMinutes),
      WeekendsCount  = countif(IsWeekend == true),
      AppVersions    = make_set(AppVersion)
    by UserId
  | where AttemptCount >= MinAttempts and DistinctDays >= MinDistinctDays
  | extend
      AlertName    = "AfterHoursWork",
      AlertDetails = strcat(
          "User ", UserId,
          " performed ", AttemptCount, " off-hours logins",
          " across ", DistinctDays, " distinct days.",
          " Max deviation from working hours: ", MaxDeviation, " min.",
          " Weekend logins: ", WeekendsCount, "."
      )
  | project
      TimeGenerated = LastSeen,
      UserId, AttemptCount, DistinctDays, MaxDeviation,
      WeekendsCount, DeviceIds, AppVersions, FirstSeen, LastSeen,
      AlertName, AlertDetails  
queryPeriod: 2d
status: Available
alertDetailsOverride:
  alertDescriptionFormat: '{{AlertDetails}}'
  alertDisplayNameFormat: 'StealthTalk: After-Hours Work - {{UserId}} ({{AttemptCount}} events / {{DistinctDays}} days)'
suppressionEnabled: false
customDetails:
  MaxDeviation: MaxDeviation
  AttemptCount: AttemptCount
  DistinctDays: DistinctDays
  LastSeen: LastSeen
  WeekendsCount: WeekendsCount
  FirstSeen: FirstSeen
tactics:
- InitialAccess
- DefenseEvasion
- Persistence
triggerOperator: gt
queryFrequency: 1h
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/StealthTalk/Analytic Rules/AfterHoursWork.yaml
entityMappings:
- fieldMappings:
  - columnName: UserId
    identifier: Name
  entityType: Account
suppressionDuration: 5h
name: StealthTalk - After hours work
triggerThreshold: 0
description: |
  Identifies systematic off-hours activity for a single StealthTalk user - repeated authentications
  outside the user's configured working hours OR on weekends, observed across at least two
  distinct calendar days within a 48-hour window. The pattern is a common indicator of credential
  misuse, insider threat, or compromise of the account by an attacker operating in a different
  timezone.

  An "off-hours event" is one where IsWeekend=true OR DeviationMinutes >= 180 (i.e. >= 3 hours
  after the configured working-hours end). Three or more such events on at least two distinct
  days are required for an incident to fire - a single late-evening login is not enough.  
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    lookbackDuration: 5h
    matchingMethod: Selected
    groupByEntities:
    - Account
    reopenClosedIncident: false
id: e3a8b2f1-5c7d-4d89-9b6e-0f1a2c3d4e5f
kind: Scheduled
relevantTechniques:
- T1078
requiredDataConnectors:
- connectorId: StealthTalkAnomalousAuth
  dataTypes:
  - StealthTalkAnomalousAuth_CL