Detection rules › Kusto
Detect port misuse by static threshold (ASIM Network Session schema)
This detection rule detects port usage above the configured threshold. The rule utilize ASIM normalization, and is applied to any source which supports the ASIM Network Session schema. To tune the rule to your environment configure it using the 'NetworkSession_Monitor_Configuration' watchlist. This rule leverages log summaries generated by a Summary Rule or Summarized Playbook. If no such summaries are available, the rule falls back to direct analysis using ASIM function.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Command & Control |
Telemetry coverage
Rule body
id: 156997bd-da0f-4729-b47a-0a3e02dd50c8
name: Detect port misuse by static threshold (ASIM Network Session schema)
description: |
'This detection rule detects port usage above the configured threshold. The rule utilize [ASIM](https://aka.ms/AboutASIM) normalization, and is applied to any source which supports the ASIM Network Session schema. To tune the rule to your environment configure it using the 'NetworkSession_Monitor_Configuration' watchlist. This rule leverages log summaries generated by a Summary Rule or Summarized Playbook. If no such summaries are available, the rule falls back to direct analysis using ASIM function.'
severity: Medium
status: Available
tags:
- Schema: ASimNetworkSessions
SchemaVersion: 0.2.4
requiredDataConnectors:
- connectorId: AWSS3
dataTypes:
- AWSVPCFlow
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftSysmonForLinux
dataTypes:
- Syslog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: AzureNSG
dataTypes:
- AzureDiagnostics
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: CiscoAsaAma
dataTypes:
- CommonSecurityLog
- connectorId: Corelight
dataTypes:
- Corelight_CL
- connectorId: AIVectraStream
dataTypes:
- VectraStream
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog
- connectorId: CiscoMeraki
dataTypes:
- Syslog
- CiscoMerakiNativePoller
queryFrequency: 20m
queryPeriod: 60m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- Execution
- InitialAccess
relevantTechniques:
- T1095
- T1059
- T1203
- T1190
query: |
let lookback = 20m;
let mapping = _GetWatchlist('NetworkSession_Monitor_Configuration')
| where Type == "Detection" and ThresholdType == "Static" and Severity != "Disabled"
| extend
Ports = split(Ports, ","),
App = split(App, ","),
Protocol = split(Protocol, ","),
Direction = split(Direction, ","),
Action = split(Action, ",")
| project
Ports,
App,
Protocol,
Direction,
Action,
Type,
ThresholdType,
Threshold,
Severity,
Tactic,
Name,
Description
| mv-expand Ports
| mv-expand App
| mv-expand Protocol
| mv-expand Direction
| mv-expand Action
| extend
Ports = tostring(Ports),
App = tostring(App),
Protocol = tostring(Protocol),
Direction = tostring(Direction),
Action = tostring(Action),
Threshold = toint(Threshold);
let NetworkSummary_Protocol = materialize(
union isfuzzy=true
(
NetworkSummary_Protocol_CL
| project v = int(2)
),
(
print int(1)
| project v = print_0
)
| summarize maxv = max(v)
| extend
NetworkSummary_Protocol_Exists = (maxv > 1),
NetworkCustomAnalyticsExists = false
);
let NetworkCustomAnalytics = materialize(
union isfuzzy=true
(
NetworkCustomAnalytics_protocol_CL
| project v = int(2)
),
(
print int(1)
| project v = print_0
)
| summarize maxv = max(v)
| extend
NetworkCustomAnalyticsExists = (maxv > 1),
NetworkSummary_Protocol_Exists = false
);
let allData = union isfuzzy=true
(
(datatable(
exists: int,
NetworkSummary_Protocol_Exists: bool,
NetworkCustomAnalyticsExists: bool
)[
1, false, false
]
| join (NetworkSummary_Protocol) on NetworkSummary_Protocol_Exists
| join (NetworkCustomAnalytics) on NetworkCustomAnalyticsExists
)
| join (
_Im_NetworkSession(starttime=bin(now(-20m), 20m), endtime=bin(now(), 20m))
| where TimeGenerated > bin(now(-20m), 20m)
| summarize Count=count()
by
NetworkProtocol,
DstPortNumber,
DstAppName,
NetworkDirection,
DvcAction,
bin(TimeGenerated, 20m)
| extend
EventTime = TimeGenerated,
Count = toint(Count),
DstPortNumber = toint(DstPortNumber),
exists=int(1)
)
on exists
| project-away exists*, maxv, NetworkSummary_Protocol_Exists*, NetworkCustomAnalyticsExists*
),
(
(datatable(exists: int, NetworkSummary_Protocol_Exists: bool)[1, false]
| join (NetworkSummary_Protocol) on NetworkSummary_Protocol_Exists
)
| join (
NetworkCustomAnalytics_protocol_CL
| where EventTime_t == toscalar(NetworkCustomAnalytics_protocol_CL
| summarize max(EventTime_t))
| project
NetworkProtocol=NetworkProtocol_s,
DstPortNumber=DstPortNumber_d,
DstAppName=DstAppName_s,
NetworkDirection=NetworkDirection_s,
DvcAction=DvcAction_s,
Count=count__d,
EventTime=EventTime_t,
TimeGenerated,
Type
| extend Count = toint(Count), DstPortNumber = toint(DstPortNumber), exists=int(1)
)
on exists
| project-away exists*, maxv, NetworkSummary_Protocol_Exists*, NetworkCustomAnalyticsExists*
),
(
NetworkSummary_Protocol_CL
| where EventTime == toscalar(NetworkSummary_Protocol_CL
| summarize max(EventTime))
| project
NetworkProtocol,
DstPortNumber,
DstAppName,
NetworkDirection,
DvcAction,
Count=count_,
EventTime,
TimeGenerated,
Type
| extend Count = toint(Count), DstPortNumber = toint(DstPortNumber)
)
| project-away exists*, maxv*, NetworkSummary_Protocol_Exists*, NetworkCustomAnalyticsExists*
;
allData
| where isnotempty(DstPortNumber)
| summarize Sum=sum(Count) by DstPortNumber, NetworkProtocol, NetworkDirection, DvcAction
| join kind=inner ['mapping'] where Ports has tostring(DstPortNumber)
| where Sum > Threshold
and (Protocol == "*" or Protocol has NetworkProtocol)
and (Direction == "*" or Direction has NetworkDirection)
and (Action == "*" or Action has DvcAction)
| project
Name,
Description,
NetworkProtocol,
DstPortNumber,
NetworkDirection,
DvcAction,
Severity,
Tactic
| summarize
NetworkProtocols=make_set_if(NetworkProtocol, isnotempty(NetworkProtocol), 20),
NetworkDirections=make_set_if(NetworkDirection, isnotempty(NetworkDirection), 5),
DvcActions=make_set_if(DvcAction, isnotempty(DvcAction), 10)
by Name, Severity, Tactic, DstPortNumber, Description
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
AllNetworkProtocols: NetworkProtocols
AllNetworkDirections: NetworkDirections
AllDvcAction: DvcActions
DstPortNumber: DstPortNumber
alertDetailsOverride:
alertDisplayNameFormat: Detected {{Name}}
alertDescriptionFormat: '{{Description}}'
alertTacticsColumnName: Tactic
alertSeverityColumnName: Severity
version: 1.2.0
kind: Scheduled
Stages and Predicates
Stage 0: let
let lookback = 20m;
let mapping = _GetWatchlist('NetworkSession_Monitor_Configuration')
| where Type == "Detection" and ThresholdType == "Static" and Severity != "Disabled"
| extend
Ports = split(Ports, ","),
App = split(App, ","),
Protocol = split(Protocol, ","),
Direction = split(Direction, ","),
Action = split(Action, ",")
| project
Ports,
App,
Protocol,
Direction,
Action,
Type,
ThresholdType,
Threshold,
Severity,
Tactic,
Name,
Description
| mv-expand Ports
| mv-expand App
| mv-expand Protocol
| mv-expand Direction
| mv-expand Action
| extend
Ports = tostring(Ports),
App = tostring(App),
Protocol = tostring(Protocol),
Direction = tostring(Direction),
Action = tostring(Action),
Threshold = toint(Threshold);
let NetworkSummary_Protocol = materialize(
union isfuzzy=true
(
NetworkSummary_Protocol_CL
| project v = int(2)
),
(
print int(1)
| project v = print_0
)
| summarize maxv = max(v)
| extend
NetworkSummary_Protocol_Exists = (maxv > 1),
NetworkCustomAnalyticsExists = false
);
let NetworkCustomAnalytics = materialize(
union isfuzzy=true
(
NetworkCustomAnalytics_protocol_CL
| project v = int(2)
),
(
print int(1)
| project v = print_0
)
| summarize maxv = max(v)
| extend
NetworkCustomAnalyticsExists = (maxv > 1),
NetworkSummary_Protocol_Exists = false
);
let allData = union <inlined as stages below>;
Stage 1: source
let allData
Stage 2: union
union of 3 branches
Stage 3: source
datatable
Stage 4: join
join (NetworkSummary_Protocol) on NetworkSummary_Protocol_Exists
Stage 5: join
join (NetworkCustomAnalytics) on NetworkCustomAnalyticsExists
Stage 6: join
join (_Im_NetworkSession) on exists
Stage 7: project-away
project-away NetworkCustomAnalyticsExists*, NetworkSummary_Protocol_Exists*, exists*, maxv
Stage 8: source
datatable
Stage 9: join
join (NetworkSummary_Protocol) on NetworkSummary_Protocol_Exists
Stage 10: join
join (NetworkCustomAnalytics_protocol_CL) on exists
Stage 11: project-away
project-away NetworkCustomAnalyticsExists*, NetworkSummary_Protocol_Exists*, exists*, maxv
Stage 12: source
NetworkSummary_Protocol_CL
Stage 13: where
where /* macro: (EventTime == toscalar(<NestedQuery>)) */
Stage 14: project
project Count, DstAppName, DstPortNumber, DvcAction, EventTime, NetworkDirection, NetworkProtocol, TimeGenerated, Type
Stage 15: extend
extend Count, DstPortNumber
Stage 16: project-away
project-away NetworkCustomAnalyticsExists*, NetworkSummary_Protocol_Exists*, exists*, maxv*
Stage 17: where
where isnotempty(DstPortNumber)
Stage 18: summarize
summarize Sum by DstPortNumber, NetworkProtocol, NetworkDirection, DvcAction
Stage 19: join
join kind=inner (mapping)
Stage 20: where
where (Action =~ DvcAction or Action == "*") and (Direction =~ NetworkDirection or Direction == "*") and (Protocol =~ NetworkProtocol or Protocol == "*") and Sum > Threshold
Stage 21: project
project Description, DstPortNumber, DvcAction, Name, NetworkDirection, NetworkProtocol, Severity, Tactic
Stage 22: summarize
summarize DvcActions, NetworkDirections, NetworkProtocols by Name, Severity, Tactic, DstPortNumber, Description
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | cross_field_compare |
| field:"Action" kind:cross_field_compare value:"DvcAction" |
Action | eq |
| field:"Action" kind:eq value:"*" |
Direction | cross_field_compare |
| field:"Direction" kind:cross_field_compare value:"NetworkDirection" |
Direction | eq |
| field:"Direction" kind:eq value:"*" |
DstPortNumber | is_not_null | field:"DstPortNumber" kind:is_not_null | |
Protocol | cross_field_compare |
| field:"Protocol" kind:cross_field_compare value:"NetworkProtocol" |
Protocol | eq |
| field:"Protocol" kind:eq value:"*" |
Severity | ne |
| field:"Severity" kind:ne value:"Disabled" |
Sum | cross_field_compare |
| field:"Sum" kind:cross_field_compare value:"Threshold" |
ThresholdType | eq |
| field:"ThresholdType" kind:eq value:"Static" |
Type | eq |
| field:"Type" kind:eq value:"Detection" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Description | summarize |
DstPortNumber | summarize |
DvcActions | summarize |
Name | summarize |
NetworkDirections | summarize |
NetworkProtocols | summarize |
Severity | summarize |
Tactic | summarize |