Detection rules › Kusto
Zero Networks Segment - Rare JIT Rule Creation
'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days. JIT Rule creations are indicated by the Activity Type Id 20'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement | T1021 Remote Services |
Rule body kusto
id: 58688058-68b2-4b39-8009-ac6dc4d81ea1
name: Zero Networks Segment - Rare JIT Rule Creation
description: |
'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days.
JIT Rule creations are indicated by the Activity Type Id 20'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ZeroNetworksSegmentAuditFunction
dataTypes:
- ZNSegmentAudit_CL
- connectorId: ZeroNetworksSegmentAuditNativePoller
dataTypes:
- ZNSegmentAuditNativePoller_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T1021
query: |
let starttime = 14d;
let endtime = 1d;
ZNSegmentAudit
| where TimeGenerated >= ago(endtime)
| where AuditTypeId == 20
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
by PerformedByName, tostring(DestinationEntityName)
// use left anti to exclude anything from the previous 14 days that is not rare
| join kind=leftanti (
ZNSegmentAudit
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| where AuditTypeId == 20
| summarize by tostring(DestinationEntityName)
) on DestinationEntityName
| summarize StartTime = min(StartTime), EndTime = max(EndTime), ConnectionCount = sum(ConnectionCount)
by PerformedByName, DestinationEntityName
| extend TimeGenerated = StartTime
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: PerformedByName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DestinationEntityName
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let starttime = 14d;
let endtime = 1d;
Stage 1: source
ZNSegmentAudit
Stage 2: where
| where TimeGenerated >= ago(endtime)
Stage 3: where
| where AuditTypeId == 20
Stage 4: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
by PerformedByName, tostring(DestinationEntityName)
Stage 5: join (negated)
| join kind=leftanti (
ZNSegmentAudit
| where TimeGenerated between (ago(starttime) .. ago(endtime))
| where AuditTypeId == 20
| summarize by tostring(DestinationEntityName)
) on DestinationEntityName
Stage 6: summarize
| summarize StartTime = min(StartTime), EndTime = max(EndTime), ConnectionCount = sum(ConnectionCount)
by PerformedByName, DestinationEntityName
Stage 7: extend
| extend TimeGenerated = StartTime
Exclusions
Top-level NOT(...) conjuncts: predicates this rule actively suppresses.
| Field | Kind | Excluded values |
|---|---|---|
AuditTypeId | eq | 20 |
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.
| Field | Kind | Values |
|---|---|---|
AuditTypeId | eq |
|
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.
| Field | Source |
|---|---|
ConnectionCount | summarize |
DestinationEntityName | summarize |
EndTime | summarize |
PerformedByName | summarize |
StartTime | summarize |
TimeGenerated | extend |