Detection rules › Kusto
TI Map IP entity to Cloud App Events
Identifies compromises and attacks and detect malicious activities in one's IP entity from TI
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | CloudAppEvents action any: Cloud app activity |
Rule body
id: 16a45aee-5e39-4d1b-b508-40f847c99353
name: TI Map IP entity to Cloud App Events
description: |
'Identifies compromises and attacks and detect malicious activities in one's IP entity from TI'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1d;
let ioc_lookBack = 14d;
let IP_Indicators = 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)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
| extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity);
// | extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity);
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
| join kind=innerunique (
CloudAppEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend CloudAppEvents_TimeGenerated = TimeGenerated) on $left.TI_ipEntity == $right.IPAddress
| where CloudAppEvents_TimeGenerated < ValidUntil
| summarize CloudAppEventsTimeGenerated = argmax(CloudAppEvents_TimeGenerated, *) by Id, IPAddress
| extend
Description = column_ifexists("max_CloudAppEvents_TimeGenerated_Description", ""),
ActivityGroupNames = column_ifexists("max_CloudAppEvents_TimeGenerated_ActivityGroupNames", ""),
ThreatType = column_ifexists("max_CloudAppEvents_TimeGenerated_ThreatType", ""),
ExpirationDateTime = column_ifexists("max_CloudAppEvents_TimeGenerated_ExpirationDateTime", ""),
ConfidenceScore = column_ifexists("max_CloudAppEvents_TimeGenerated_ConfidenceScore", ""),
TI_ipEntity = column_ifexists("max_CloudAppEvents_TimeGenerated_TI_ipEntity", ""),
NetworkDestinationIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkDestinationIP", ""),
NetworkSourceIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkSourceIP", ""),
EmailSourceIPAddress = column_ifexists("max_CloudAppEvents_TimeGenerated_EmailSourceIpAddress", "")
| project CloudAppEventsTimeGenerated, Description, ActivityGroupNames, Id, ThreatType, ExpirationDateTime, ConfidenceScore, TI_ipEntity, NetworkDestinationIP, NetworkSourceIP, EmailSourceIPAddress
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: TI_ipEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NetworkDestinationIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NetworkSourceIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: EmailSourceIPAddress
version: 1.0.8
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1d;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 10 define let IP_Indicators (the rule's main pipeline source); stages 11 to 16 run on it.
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 3: where
| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
Stage 4: extend
| extend NetworkSourceIP = toupper(ObservableValue)
Stage 5: extend
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
Stage 6: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 7: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 8: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 9: extend
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
TI_ipEntity =if
isnotempty(NetworkSourceIP)NetworkSourceIPelse
NetworkSourceIPStage 10: extend
| extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
TIipEntity =if
isempty(TI_ipEntity) and isnotempty(NetworkSourceIP)NetworkSourceIPelse
TI_ipEntityStage 11: project-reorder
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
Stage 12: join
| join kind=innerunique (
CloudAppEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend CloudAppEvents_TimeGenerated = TimeGenerated) on $left.TI_ipEntity == $right.IPAddress
Stage 13: where
| where CloudAppEvents_TimeGenerated < ValidUntil
Stage 14: summarize
| summarize CloudAppEventsTimeGenerated = argmax(CloudAppEvents_TimeGenerated, *) by Id, IPAddress
Stage 15: extend
| extend
Description = column_ifexists("max_CloudAppEvents_TimeGenerated_Description", ""),
ActivityGroupNames = column_ifexists("max_CloudAppEvents_TimeGenerated_ActivityGroupNames", ""),
ThreatType = column_ifexists("max_CloudAppEvents_TimeGenerated_ThreatType", ""),
ExpirationDateTime = column_ifexists("max_CloudAppEvents_TimeGenerated_ExpirationDateTime", ""),
ConfidenceScore = column_ifexists("max_CloudAppEvents_TimeGenerated_ConfidenceScore", ""),
TI_ipEntity = column_ifexists("max_CloudAppEvents_TimeGenerated_TI_ipEntity", ""),
NetworkDestinationIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkDestinationIP", ""),
NetworkSourceIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkSourceIP", ""),
EmailSourceIPAddress = column_ifexists("max_CloudAppEvents_TimeGenerated_EmailSourceIpAddress", "")
Stage 16: project
| project CloudAppEventsTimeGenerated, Description, ActivityGroupNames, Id, ThreatType, ExpirationDateTime, ConfidenceScore, TI_ipEntity, NetworkDestinationIP, NetworkSourceIP, EmailSourceIPAddress
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CloudAppEvents_TimeGenerated | cross_field_compare |
| field:"CloudAppEvents_TimeGenerated" kind:cross_field_compare value:"ValidUntil" |
IndicatorType | in |
| field:"IndicatorType" kind:in |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
CloudAppEventsTimeGenerated | project |
ConfidenceScore | project |
Description | project |
EmailSourceIPAddress | project |
ExpirationDateTime | project |
Id | project |
NetworkDestinationIP | project |
NetworkSourceIP | project |
TI_ipEntity | project |
ThreatType | project |