Detection rules › Kusto

TI Map IP entity to DNS Events (ASIM DNS schema)

Severity
medium
Time window
14d
Group by
Id, IoC, ObservableValue
Source
github.com/Azure/Azure-Sentinel

This rule identifies DNS requests for which response IP address is a known IoC. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM DNS schema.

MITRE ATT&CK coverage

TacticTechniques
Command & Control

Telemetry coverage

ProviderRecord / event type
SysmonEvent ID 22: DNSEvent (DNS query)

Rule body

id: b306fba8-1a28-449f-aa24-30362e16d4f5
name: TI Map IP entity to DNS Events (ASIM DNS schema)
description: |
  'This rule identifies DNS requests for which response IP address is a known IoC. This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM DNS schema.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: DNS
    dataTypes:
      - DnsEvents
  - connectorId: AzureFirewall
    dataTypes: 
      - AzureDiagnostics
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: InfobloxNIOS
    dataTypes: 
      - Syslog
  - connectorId: GCPDNSDataConnector
    dataTypes: 
      - GCP_DNS_CL
  - connectorId: NXLogDnsLogs
    dataTypes: 
      - NXLog_DNS_Server_CL
  - connectorId: CiscoUmbrellaDataConnector
    dataTypes: 
      - Cisco_Umbrella_dns_CL
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: Corelight
    dataTypes: 
      - Corelight_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
tags:
  - ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/ThreatIntelligenceIndicator/IPEntity_DnsEvents.yaml
    version: 1.0.0
  - Schema: ASIMDns
    SchemaVersion: 0.1.1
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let IP_TI = 
  ThreatIntelIndicators
  | where TimeGenerated >= ago(ioc_lookBack)
  //extract key part of kv pair
  | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
  | where IndicatorType == "ipv4-addr"
  | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
  | extend NetworkSourceIP = ObservableValue
  | extend IoC = NetworkSourceIP
  | extend IndicatorId = tostring(split(Id, "--")[2])
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
  | where IsActive and (ValidUntil > now() or isempty(ValidUntil));
  IP_TI
   | project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, IoC
  | join kind=innerunique // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  (
  _Im_Dns(starttime=ago(dt_lookBack))
  | where isnotempty(DnsResponseName)
  | summarize imDns_mintime=min(TimeGenerated), imDns_maxtime=max(TimeGenerated) by SrcIpAddr, DnsQuery, DnsResponseName, Dvc, EventProduct, EventVendor
  | extend addresses = extract_all (@'(\d+\.\d+\.\d+\.\d+)', DnsResponseName)
  | mv-expand IoC = addresses to typeof(string)
  )
  on IoC
  | where imDns_mintime < ValidUntil
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | extend ThreatType = tostring(Data.indicator_types[0])
  | project imDns_mintime, imDns_maxtime, Description, ActivityGroupNames, IndicatorId, ThreatType, LatestIndicatorTime, ValidUntil, Confidence, 
  SrcIpAddr, IoC, Dvc, EventVendor, EventProduct, DnsQuery, DnsResponseName, Type
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Dvc
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IoC
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
customDetails:
  LatestIndicatorTime: LatestIndicatorTime
  Description: Description
  ActivityGroupNames: ActivityGroupNames
  IndicatorId: IndicatorId
  ThreatType: ThreatType
  ExpirationDateTime: ValidUntil
  ConfidenceScore: Confidence
  DNSRequestTime: imDns_mintime
  SourceIPAddress: SrcIpAddr
  DnsQuery: DnsQuery
alertDetailsOverride:
  alertDisplayNameFormat: The response {{IoC}} to DNS query matched an IoC 
  alertDescriptionFormat: The response address {{IoC}} to a DNS query matched a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence blade for more information on the indicator.
version: 1.2.11
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

let IP_TI is inlined into the numbered stages below.

Stages 1 to 7 define let IP_TI (the rule's main pipeline source); stages 8 to 12 run on it.

Stage 1: source

ThreatIntelIndicators

Stage 2: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 3: extend

| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))

Stage 4: where

| where IndicatorType == "ipv4-addr"

Stage 5: extend (4 consecutive steps)

| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend NetworkSourceIP = ObservableValue
| extend IoC = NetworkSourceIP
| extend IndicatorId = tostring(split(Id, "--")[2])

Stage 6: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue

Stage 7: where

| where IsActive and (ValidUntil > now() or isempty(ValidUntil))

Stage 8: project-reorder

IP_TI
| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, IoC

Stage 9: join

| join kind=innerunique
(
_Im_Dns(starttime=ago(dt_lookBack))
| where isnotempty(DnsResponseName)
| summarize imDns_mintime=min(TimeGenerated), imDns_maxtime=max(TimeGenerated) by SrcIpAddr, DnsQuery, DnsResponseName, Dvc, EventProduct, EventVendor
| extend addresses = extract_all (@'(\d+\.\d+\.\d+\.\d+)', DnsResponseName)
| mv-expand IoC = addresses to typeof(string)
)
on IoC

Stage 10: where

| where imDns_mintime < ValidUntil

Stage 11: extend (3 consecutive steps)

| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| extend ThreatType = tostring(Data.indicator_types[0])

Stage 12: project

| project imDns_mintime, imDns_maxtime, Description, ActivityGroupNames, IndicatorId, ThreatType, LatestIndicatorTime, ValidUntil, Confidence, 
SrcIpAddr, IoC, Dvc, EventVendor, EventProduct, DnsQuery, DnsResponseName, Type

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
Confidenceproject
Descriptionproject
DnsQueryproject
DnsResponseNameproject
Dvcproject
EventProductproject
EventVendorproject
IndicatorIdproject
IoCproject
LatestIndicatorTimeproject
SrcIpAddrproject
ThreatTypeproject
Typeproject
ValidUntilproject
imDns_maxtimeproject
imDns_mintimeproject