Detection rules › Kusto

Claroty - Login to uncommon location

Status
available
Severity
medium
Time window
14d
Group by
EventMessage, EventType, Site, SrcIpAddr, SrcUsername
Source
github.com/Azure/Azure-Sentinel

Detects user login to uncommon location.

MITRE ATT&CK coverage

Rule body kusto

id: e7dbcbc3-b18f-4635-b27c-718195c369f1
name: Claroty - Login to uncommon location
description: 'Detects user login to uncommon location.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let usr_sites = ClarotyEvent
  | where TimeGenerated > ago(14d)
  | where EventType has 'Login to SRA succeeded'
  | extend Site = column_ifexists("site_name","")
  | where isnotempty(Site)
  | extend SrcUsername = extract(@'User\s(.*?)\slogged', 1, EventMessage)
  | where isnotempty(SrcUsername)
  | summarize all_loc = make_set(tostring(Site)) by SrcUsername;
  ClarotyEvent
  | where TimeGenerated > ago(14d)
  | where EventType has 'Login to SRA succeeded'
  | extend Site = column_ifexists("site_name","")
  | where isnotempty(Site)
  | extend SrcUsername = extract(@'User\s(.*?)\slogged', 1, EventMessage)
  | where isnotempty(SrcUsername)
  | join kind=leftouter (usr_sites) on SrcUsername
  | where isempty(all_loc) or not(set_has_element(all_loc, tostring(Site)))
  | extend SrcIpAddr
  | summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), EventCount=count(), Sites=make_set(Site, 100), KnownSites=any(all_loc) by SrcUsername, SrcIpAddr, Site, EventType, EventMessage
  | extend UncommonSiteCount = array_length(Sites)
  | project StartTime, EndTime, SrcUsername, SrcIpAddr, Site, EventType, EventMessage, EventCount, UncommonSiteCount, KnownSites
entityMappings:
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr
version: 1.0.4
kind: Scheduled

Stages and Predicates

Let binding: usr_sites

let usr_sites = ClarotyEvent
| where TimeGenerated > ago(14d)
| where EventType has 'Login to SRA succeeded'
| extend Site = column_ifexists("site_name","")
| where isnotempty(Site)
| extend SrcUsername = extract(@'User\s(.*?)\slogged', 1, EventMessage)
| where isnotempty(SrcUsername)
| summarize all_loc = make_set(tostring(Site)) by SrcUsername;

Stage 1: source

ClarotyEvent

Stage 2: where

| where TimeGenerated > ago(14d)

Stage 3: where

| where EventType has 'Login to SRA succeeded'

Stage 4: extend

| extend Site = column_ifexists("site_name","")

Stage 5: where

| where isnotempty(Site)

Stage 6: extend

| extend SrcUsername = extract(@'User\s(.*?)\slogged', 1, EventMessage)

Stage 7: where

| where isnotempty(SrcUsername)

Stage 8: join

| join kind=leftouter (usr_sites) on SrcUsername

Stage 9: where

| where isempty(all_loc) or not(set_has_element(all_loc, tostring(Site)))

Stage 10: extend

| extend SrcIpAddr

Stage 11: summarize

| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), EventCount=count(), Sites=make_set(Site, 100), KnownSites=any(all_loc) by SrcUsername, SrcIpAddr, Site, EventType, EventMessage

Stage 12: extend

| extend UncommonSiteCount = array_length(Sites)

Stage 13: project

| project StartTime, EndTime, SrcUsername, SrcIpAddr, Site, EventType, EventMessage, EventCount, UncommonSiteCount, KnownSites

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.

FieldKindValues
EventTypematch
  • Login to SRA succeeded transforms: term
Siteis_not_null
  • (no value, null check)
SrcUsernameis_not_null
  • (no value, null check)
all_locis_null
  • (no value, null check)

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
EndTimeproject
EventCountproject
EventMessageproject
EventTypeproject
KnownSitesproject
Siteproject
SrcIpAddrproject
SrcUsernameproject
StartTimeproject
UncommonSiteCountproject