Detection rules › Kusto

User joining Zoom meeting from suspicious timezone

Severity
low
Time window
14d
Group by
MeetingId
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'The alert shows users that join a Zoom meeting from a time zone other than the one the meeting was created in. You can also whitelist known good time zones in the tz_whitelist value using the tz database name format https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'

MITRE ATT&CK coverage

TacticTechniques
Initial Access
Privilege Escalation

Rule body

id: 58fc0170-0877-4ea8-a9ff-d805e361cfae
name: User joining Zoom meeting from suspicious timezone
description: |
  'The alert shows users that join a Zoom meeting from a time zone other than the one the meeting was created in.
  You can also whitelist known good time zones in the tz_whitelist value using the tz database name format https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'
severity: Low
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - PrivilegeEscalation
relevantTechniques:
  - T1078
query: |
  let schedule_lookback = 14d;
  let join_lookback = 1d;
  // If you want to whitelist specific timezones include them in a list here
  let tz_whitelist = dynamic([]);
  let meetings = (
  ZoomLogs
  | where TimeGenerated >= ago(schedule_lookback)
  | where Event =~ "meeting.created"
  | extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
  | extend SchedTimezone = tostring(parse_json(MeetingEvents).Timezone));
  ZoomLogs
  | where TimeGenerated >= ago(join_lookback)
  | where Event =~ "meeting.participant_joined"
  | extend JoinedTimeZone = tostring(parse_json(MeetingEvents).Timezone)
  | extend MeetingName = tostring(parse_json(MeetingEvents).MeetingName)
  | extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
  | where JoinedTimeZone !in (tz_whitelist)
  | join (meetings) on MeetingId
  | where SchedTimezone != JoinedTimeZone
  | project TimeGenerated, MeetingName, JoiningUser=payload_object_participant_user_name_s, JoinedTimeZone, SchedTimezone, MeetingScheduler=User1
  | extend AccountName = tostring(split(JoiningUser, "@")[0]), AccountUPNSuffix = tostring(split(JoiningUser, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: JoiningUser
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
version: 1.0.4
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Parameters

let schedule_lookback = 14d;
let join_lookback = 1d;
let tz_whitelist = dynamic([]);

Let binding: meetings used in Stage 6

let meetings = (
ZoomLogs
| where TimeGenerated >= ago(schedule_lookback)
| where Event =~ "meeting.created"
| extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
| extend SchedTimezone = tostring(parse_json(MeetingEvents).Timezone));

Stage 1: source

ZoomLogs

Stage 2: where

| where TimeGenerated >= ago(join_lookback)

Stage 3: where

| where Event =~ "meeting.participant_joined"

Stage 4: extend (3 consecutive steps)

| extend JoinedTimeZone = tostring(parse_json(MeetingEvents).Timezone)
| extend MeetingName = tostring(parse_json(MeetingEvents).MeetingName)
| extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)

Stage 5: where

| where JoinedTimeZone !in (tz_whitelist)

Stage 6: join

| join (meetings) on MeetingId

Stage 7: where

| where SchedTimezone != JoinedTimeZone

Stage 8: project

| project TimeGenerated, MeetingName, JoiningUser=payload_object_participant_user_name_s, JoinedTimeZone, SchedTimezone, MeetingScheduler=User1

Stage 9: extend

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

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
JoinedTimeZoneeq[]excludes:JoinedTimeZone field:"JoinedTimeZone" value:"[]"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Eventeq
  • meeting.created
  • meeting.participant_joined
field:"Event" kind:eq
SchedTimezonecross_field_compare
  • JoinedTimeZone transforms: op:ne
field:"SchedTimezone" kind:cross_field_compare value:"JoinedTimeZone"

Output fields

These fields are emitted when the rule matches.

FieldSource
JoinedTimeZoneproject
JoiningUserproject
MeetingNameproject
MeetingSchedulerproject
SchedTimezoneproject
TimeGeneratedproject
AccountNameextend
AccountUPNSuffixextend