There appears to be a logic flaw in the "TI Map IP Entity to CommonSecurityLog" rule.
The current implementation only takes into account the SourceIP when mapping to CS_ipEntity, which results in the DestinationIP being omitted.
In firewall logs, both SourceIP and DestinationIP are present, and due to the existing if condition, when SourceIP is assigned to CS_ipEntity, the DestinationIP is not matched with threat intelligence.
This causes detections based on DestinationIP to be missed.
Updated Query:
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_Indicators = ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, "NO_IP")
| where TI_ipEntity != "NO_IP"
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127.";
let CS_logs = CommonSecurityLog
| where TimeGenerated >= ago(dt_lookBack)
| extend MessageIP = extract_all(@'(\d+\.\d+\.\d+\.\d+)', Message)
| extend CS_ipEntity1 = iff(isnotempty(SourceIP), SourceIP, iff(isnotempty(MessageIP), MessageIP[0], ""))
| extend CS_ipEntity2 = iff(isnotempty(DestinationIP), DestinationIP, "")
| extend CommonSecurityLog_TimeGenerated = TimeGenerated;
let join_CS_ipEntity1 = IP_Indicators
| join kind=innerunique (CS_logs) on $left.TI_ipEntity == $right.CS_ipEntity1
| where CommonSecurityLog_TimeGenerated < ExpirationDateTime
| extend MatchedEntity = iff(TI_ipEntity == SourceIP, "SourceIP", "MessageIP");
let join_CS_ipEntity2 = IP_Indicators
| join kind=innerunique (CS_logs) on $left.TI_ipEntity == $right.CS_ipEntity2
| where CommonSecurityLog_TimeGenerated < ExpirationDateTime
| extend MatchedEntity = "DestinationIP";
join_CS_ipEntity1
| union join_CS_ipEntity2
| summarize CommonSecurityLog_TimeGenerated = arg_max(CommonSecurityLog_TimeGenerated, *) by IndicatorId, CS_ipEntity1, CS_ipEntity2, MatchedEntity
| project timestamp = CommonSecurityLog_TimeGenerated, SourceIP, DestinationIP, MessageIP, Message, DeviceVendor, DeviceProduct, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, TI_ipEntity, CS_ipEntity1, CS_ipEntity2, MatchedEntity, LogSeverity, DeviceAction, Type
Issue:
There appears to be a logic flaw in the "TI Map IP Entity to CommonSecurityLog" rule.
SourceIP
when mapping toCS_ipEntity
, which results in theDestinationIP
being omitted.SourceIP
andDestinationIP
are present, and due to the existing if condition, whenSourceIP
is assigned toCS_ipEntity
, theDestinationIP
is not matched with threat intelligence.DestinationIP
to be missed.Updated Query:
https://github.com/Azure/Azure-Sentinel/blob/8346b62bed3af440eb004ab84e9fa34095fb77d7/Solutions/Threat%20Intelligence/Analytic%20Rules/IPEntity_CustomSecurityLog.yaml#L48C9-L48C82