Detection rules › Kusto

Zscaler - Shared ZPA session

Status
available
Severity
high
Time window
1d
Group by
DstUserName, NetworkSessionId, SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

'Detects shared ZPA session.'

MITRE ATT&CK coverage

Rule body

id: 40a98355-0e52-479f-8c91-4ab659cba878
name: Zscaler - Shared ZPA session
description: |
  'Detects shared ZPA session.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - ZPA_CL
queryFrequency: 1h
queryPeriod: 24h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
  - T1133
query: |
  let open_sessions =
  ZPAEvent
  | where DvcAction == 'open'
  | summarize timestampstart = min(TimeGenerated) by DstUserName, SrcIpAddr, NetworkSessionId
  | sort by timestampstart asc;
  let closed_sessions =
  ZPAEvent
  | where TimeGenerated > ago(1h)
  | where DvcAction == 'close'
  | summarize timestampend = max(TimeGenerated) by DstUserName, SrcIpAddr, NetworkSessionId
  | sort by timestampend asc;
  open_sessions
  | join (closed_sessions) on DstUserName
  | sort by DstUserName, timestampstart
  | extend prev_session_closetime = prev(timestampend,1)
  | extend prev_session_starttime = prev(timestampstart,1)
  | extend PreviousSrcIpAddr = prev(SrcIpAddr, 1)
  | extend prev_sessionuser = prev(DstUserName, 1) 
  | where DstUserName == prev_sessionuser
  | where SrcIpAddr != PreviousSrcIpAddr
  | where prev_session_closetime > timestampstart
  | project DstUserName, SrcIpAddr, PreviousSrcIpAddr
  | extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

let open_sessions is inlined into the numbered stages below.

Let binding: closed_sessions used in Stage 5

let closed_sessions = ZPAEvent
| where TimeGenerated > ago(1h)
| where DvcAction == 'close'
| summarize timestampend = max(TimeGenerated) by DstUserName, SrcIpAddr, NetworkSessionId
| sort by timestampend asc;

Stages 1 to 4 define let open_sessions (the rule's main pipeline source); stages 5 to 12 run on it.

Stage 1: source

ZPAEvent

Stage 2: where

| where DvcAction == 'open'

Stage 3: summarize

| summarize timestampstart = min(TimeGenerated) by DstUserName, SrcIpAddr, NetworkSessionId

Stage 4: sort

| sort by timestampstart asc

Stage 5: join

open_sessions
| join (closed_sessions) on DstUserName

Stage 6: sort

| sort by DstUserName, timestampstart

Stage 7: extend (4 consecutive steps)

| extend prev_session_closetime = prev(timestampend,1)
| extend prev_session_starttime = prev(timestampstart,1)
| extend PreviousSrcIpAddr = prev(SrcIpAddr, 1)
| extend prev_sessionuser = prev(DstUserName, 1)

Stage 8: where

| where DstUserName == prev_sessionuser

Stage 9: where

| where SrcIpAddr != PreviousSrcIpAddr

Stage 10: where

| where prev_session_closetime > timestampstart

Stage 11: project

| project DstUserName, SrcIpAddr, PreviousSrcIpAddr

Stage 12: extend

| extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = DstUserName

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
DstUserNameproject
PreviousSrcIpAddrproject
SrcIpAddrproject
AccountCustomEntityextend
IPCustomEntityextend