Detection rules › Kusto

GitHub Activites from a New Country

Status
available
Severity
medium
Time window
7d
Group by
Actor
Source
github.com/Azure/Azure-Sentinel

'Detect activities from a location that was not recently or was never visited by the user or by any user in your organization.'

MITRE ATT&CK coverage

TacticTechniques
Initial AccessT1078 Valid Accounts

Rule body kusto

id: f041e01d-840d-43da-95c8-4188f6cef546
name: GitHub Activites from a New Country
description: |
  'Detect activities from a location that was not recently or was never visited by the user or by any user in your organization.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  let LearningPeriod = 7d;
  let RunTime = 1h;
  let StartTime = 1h;
  let EndRunTime = StartTime - RunTime;
  let EndLearningTime = StartTime + LearningPeriod;
  let GitHubCountryCodeLogs = (GitHubAuditData
  | where Country != "");
    GitHubCountryCodeLogs
  | where TimeGenerated between (ago(EndLearningTime) .. ago(StartTime))
  | summarize makeset(Country) by Actor
  | join kind=innerunique (
    GitHubCountryCodeLogs
    | where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
    | distinct Country, Actor, TimeGenerated
  ) on Actor 
  | where set_Country !contains Country
  | extend timestamp = TimeGenerated
  | extend AccountName = tostring(split(Actor, "@")[0]), AccountUPNSuffix = tostring(split(Actor, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Actor
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let LearningPeriod = 7d;
let RunTime = 1h;
let StartTime = 1h;
let EndRunTime = StartTime - RunTime;
let EndLearningTime = StartTime + LearningPeriod;

The stages below define let GitHubCountryCodeLogs (the rule's main pipeline source).

Stage 1: source

GitHubAuditData

Stage 2: where

| where Country != ""

The stages below run on GitHubCountryCodeLogs (the outer pipeline).

Stage 3: where

GitHubCountryCodeLogs
| where TimeGenerated between (ago(EndLearningTime) .. ago(StartTime))

Stage 4: summarize

| summarize makeset(Country) by Actor

Stage 5: join

| join kind=innerunique (
  GitHubCountryCodeLogs
  | where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
  | distinct Country, Actor, TimeGenerated
) on Actor

Stage 6: where

| where set_Country !contains Country

Stage 7: extend

| extend timestamp = TimeGenerated

Stage 8: extend

| extend AccountName = tostring(split(Actor, "@")[0]), AccountUPNSuffix = tostring(split(Actor, "@")[1])

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
set_CountrycontainsCountry

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.

FieldSource
Actorsummarize
timestampextend
AccountNameextend
AccountUPNSuffixextend