Detection rules › Kusto

Privileged User Logon from new ASN

Severity
medium
Time window
7d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'Detects a successful logon by a privileged account from an ASN not logged in from in the last 14 days. Monitor these logons to ensure they are legitimate and identify if there are any similar sign ins.'

MITRE ATT&CK coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body kusto

id: 55073036-bb86-47d3-a85a-b113ac3d9396
name: Privileged User Logon from new ASN
description: |
  'Detects a successful logon by a privileged account from an ASN not logged in from in the last 14 days.
    Monitor these logons to ensure they are legitimate and identify if there are any similar sign ins.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: BehaviorAnalytics
    dataTypes:
      - BehaviorAnalytics
  - connectorId: BehaviorAnalytics
    dataTypes:
      - IdentityInfo
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1078.004
tags:
  - AADSecOpsGuide
query: |
  let admins=(IdentityInfo
    | where AssignedRoles contains "admin" or GroupMembership has "Admin"
    | summarize by tolower(AccountUPN));
    let known_asns = (
    SigninLogs
    | where TimeGenerated between(ago(14d)..ago(1d))
    | where ResultType == 0
    | summarize by AutonomousSystemNumber);
    SigninLogs
    | where TimeGenerated > ago(1d)
    | where ResultType == 0
    | where tolower(UserPrincipalName) in (admins)
    | where AutonomousSystemNumber !in (known_asns)
    | project-reorder TimeGenerated, UserPrincipalName, UserAgent, IPAddress, AutonomousSystemNumber
    | extend AccountName = tostring(split(UserPrincipalName, "@")[0]), AccountUPNSuffix = tostring(split(UserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
version: 1.0.6
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Identity", "Security - Others" ]

Stages and Predicates

Let binding: admins

let admins = (IdentityInfo
  | where AssignedRoles contains "admin" or GroupMembership has "Admin"
  | summarize by tolower(AccountUPN));

Let binding: known_asns

let known_asns = (
  SigninLogs
  | where TimeGenerated between(ago(14d)..ago(1d))
  | where ResultType == 0
  | summarize by AutonomousSystemNumber);

Stage 1: source

SigninLogs

Stage 2: where

| where TimeGenerated > ago(1d)

Stage 3: where

| where ResultType == 0

Stage 4: where

| where tolower(UserPrincipalName) in (admins)

References admins (defined above).

Stage 5: where

| where AutonomousSystemNumber !in (known_asns)

References known_asns (defined above).

Stage 6: project-reorder

| project-reorder TimeGenerated, UserPrincipalName, UserAgent, IPAddress, AutonomousSystemNumber

Stage 7: extend

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

Exclusions

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

FieldKindExcluded values
AutonomousSystemNumbereqknown_asns

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
ResultTypeeq
  • 0 transforms: cased
UserPrincipalNamein
  • admins transforms: tolower, cased

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
AccountNameextend
AccountUPNSuffixextend