Detection rules › Kusto

Potential DGA detected (ASIM DNS Schema)

Severity
medium
Time window
10d
Group by
SrcIpAddr, dcount_sld
Author
Yaron
Source
github.com/Azure/Azure-Sentinel

Identifies clients with a high NXDomain count which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). Alert is generated when a new IP address is seen (based on not being seen associated with NXDomain records in prior 10-day baseline period). 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: 983a6922-894d-413c-9f04-d7add0ecc307
name: Potential DGA detected (ASIM DNS Schema)
description: |
  'Identifies clients with a high NXDomain count which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). Alert is generated when a new IP address is seen (based on not being seen associated with 
  NXDomain records in prior 10-day baseline period).
  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: 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: Corelight
    dataTypes: 
      - Corelight_CL

queryFrequency: 1d
queryPeriod: 10d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1568
  - T1008
tags:
  - ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/DnsEvents/DNS_HighNXDomainCount_detection.yaml
    version: 1.0.0
  - Schema: ASIMDns
    SchemaVersion: 0.1.1
query: |
  let referencestarttime = 10d;
  let referenceendtime = 1d;
  let threshold = 100;
  let nxDomainDnsEvents = (stime:datetime, etime:datetime) 
    {_Im_Dns(responsecodename='NXDOMAIN', starttime=stime, endtime=etime)
    | where DnsQueryTypeName in ("A", "AAAA")
    | where ipv4_is_match("127.0.0.1", SrcIpAddr) == False
    | where DnsQuery !contains "/" and  DnsQuery contains "."};
  nxDomainDnsEvents (stime=ago(referenceendtime) ,etime=now())
    | extend sld = tostring(split(DnsQuery, ".")[-2])
    | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(sld) by SrcIpAddr
    | where dcount_sld > threshold
    // Filter out previously seen IPs
    | join kind=leftanti (nxDomainDnsEvents (stime=ago(referencestarttime), etime=ago(referenceendtime))
      | extend sld = tostring(split(DnsQuery, ".")[-2])
      | summarize dcount(sld) by SrcIpAddr
      | where dcount_sld > threshold ) on SrcIpAddr
  // Pull out sample NXDomain responses for those remaining potentially infected IPs
  | join kind = inner (nxDomainDnsEvents (stime=ago(referencestarttime), etime=now()) | summarize by DnsQuery, SrcIpAddr) on SrcIpAddr
  | summarize StartTime = min(StartTime), EndTime = max(EndTime), sampleNXDomainList=make_list(DnsQuery, 100)  by SrcIpAddr, dcount_sld
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
version: 1.3.4
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Yaron
    support:
        tier: Community
    categories:
        domains: [ "Security - Network" ]

Stages and Predicates

Parameters

let referencestarttime = 10d;
let referenceendtime = 1d;
let threshold = 100;

let nxDomainDnsEvents is inlined into the numbered stages below.

Stages 1 to 4 define let nxDomainDnsEvents (the rule's main pipeline source); stages 5 to 10 run on it.

Stage 1: source

_Im_Dns(responsecodename='NXDOMAIN', starttime=stime, endtime=etime)

Stage 2: where

| where DnsQueryTypeName in ("A", "AAAA")

Stage 3: where

| where ipv4_is_match("127.0.0.1", SrcIpAddr) == False

Stage 4: where

| where DnsQuery !contains "/" and  DnsQuery contains "."

Stage 5: extend

nxDomainDnsEvents
| extend sld = tostring(split(DnsQuery, ".")[-2])

Stage 6: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), dcount(sld) by SrcIpAddr

Stage 7: where

| where dcount_sld > threshold

Stage 8: join (negated)

| join kind=leftanti (nxDomainDnsEvents (stime=ago(referencestarttime), etime=ago(referenceendtime))
    | extend sld = tostring(split(DnsQuery, ".")[-2])
    | summarize dcount(sld) by SrcIpAddr
    | where dcount_sld > threshold ) on SrcIpAddr

Stage 9: join

| join kind = inner (nxDomainDnsEvents (stime=ago(referencestarttime), etime=now()) | summarize by DnsQuery, SrcIpAddr) on SrcIpAddr

Stage 10: summarize

| summarize StartTime = min(StartTime), EndTime = max(EndTime), sampleNXDomainList=make_list(DnsQuery, 100)  by SrcIpAddr, dcount_sld

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DnsQuerycontains
  • . corpus 7 (elastic 4, kusto 2, splunk 1)
field:"QueryName" kind:contains value:"."
DnsQueryTypeNamein
  • A
  • AAAA
field:"DnsQueryTypeName" kind:in
dcount_sldgt
  • 100 corpus 2 (kusto 2)
field:"dcount_sld" kind:gt value:"100"

Output fields

These fields are emitted when the rule matches.

FieldSource
EndTimesummarize
SrcIpAddrsummarize
StartTimesummarize
dcount_sldsummarize
sampleNXDomainListsummarize