Detection rules › Kusto
TI Map IP Entity to DeviceNetworkEvents
Identifies a match in DeviceNetworkEvents Event data from any IP Indicator from TI.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceNetworkEvents action any: Network activity |
Rule body
id: 2474343c-9135-42ec-9c40-a1bace43da5c
name: TI Map IP Entity to DeviceNetworkEvents
description: |
'Identifies a match in DeviceNetworkEvents Event data from any IP Indicator from TI.'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DeviceNetworkEvents_ = DeviceNetworkEvents
| where isnotempty(RemoteIP)
| where TimeGenerated > ago(dt_lookBack)
| where ActionType !has "ConnectionFailed"
| extend isPrivate = ipv4_is_private(RemoteIP)
| where isPrivate != true;
let IPs = DeviceNetworkEvents_ | distinct RemoteIP | summarize make_list(RemoteIP);
ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
| extend NetworkSourceIP = toupper(ObservableValue)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
| extend TI_ipEntity = NetworkSourceIP
| where TI_ipEntity in (IPs)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend Description = tostring(parse_json(Data).description)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| join kind=innerunique (DeviceNetworkEvents_) on $left.TI_ipEntity == $right.RemoteIP
| summarize TimeGenerated = arg_max(TimeGenerated, *) by Id, TI_ipEntity, DeviceName
| extend timestamp = TimeGenerated, Name = tostring(split(InitiatingProcessAccountUpn, '@', 0)[0]), UPNSuffix = tostring(split(InitiatingProcessAccountUpn, '@', 1)[0])
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: TI_ipEntity
- entityType: URL
fieldMappings:
- identifier: Url
columnName: RemoteUrl
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Let binding: DeviceNetworkEvents_
let DeviceNetworkEvents_ = DeviceNetworkEvents
| where isnotempty(RemoteIP)
| where TimeGenerated > ago(dt_lookBack)
| where ActionType !has "ConnectionFailed"
| extend isPrivate = ipv4_is_private(RemoteIP)
| where isPrivate != true;
Let binding: IPs
let IPs = DeviceNetworkEvents_ | distinct RemoteIP | summarize make_list(RemoteIP);
Stage 1: source
let DeviceNetworkEvents_
Stage 2: source
let IPs
Stage 3: source
ThreatIntelIndicators
Stage 4: extend
extend IndicatorType
Stage 5: where
where IndicatorType in~ ("ipv4-addr", "ipv6-addr", "network-traffic")
Stage 6: extend
extend NetworkSourceIP
Stage 7: extend
extend TrafficLightProtocolLevel
Stage 8: where
where TimeGenerated >= ago(1209600s)
Stage 9: extend
extend TI_ipEntity
Stage 10: where
where TI_ipEntity =~ "IPs"
Stage 11: summarize
summarize LatestIndicatorTime by Id, ObservableValue
Stage 12: where
where (isempty(ValidUntil) or ValidUntil > now())
Stage 13: extend
extend Description
Stage 14: where
where not (Description contains "State: falsepos;") and not (Description contains "State: inactive;")
Stage 15: join
join kind=innerunique (DeviceNetworkEvents_) on TI_ipEntity, RemoteIP
Stage 16: summarize
summarize TimeGenerated by Id, TI_ipEntity, DeviceName
Stage 17: extend
extend Name, UPNSuffix, timestamp
Stage 18: project-reorder
project-reorder
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Description | contains | State: falsepos; | excludes:Description field:"Description" value:"State: falsepos;" |
Description | contains | State: inactive; | excludes:Description field:"Description" value:"State: inactive;" |
ActionType | match | ConnectionFailed | excludes:ActionType field:"ActionType" value:"ConnectionFailed" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | in |
| field:"IndicatorType" kind:in |
RemoteIP | is_not_null | field:"dest_ip" kind:is_not_null | |
TI_ipEntity | in |
| field:"TI_ipEntity" kind:in value:"IPs" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null | |
isPrivate | ne |
| field:"isPrivate" kind:ne value:"true" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceName | summarize |
Id | summarize |
TI_ipEntity | summarize |
TimeGenerated | summarize |
Name | extend |
UPNSuffix | extend |
timestamp | extend |