Detect excessive NXDOMAIN DNS queries - Anomaly based ASIM DNS Solution
| Id | 02f23312-1a33-4390-8b80-f7cd4df4dea0 | 
| Rulename | Detect excessive NXDOMAIN DNS queries - Anomaly based (ASIM DNS Solution) | 
| Description | This rule makes use of the series decompose anomaly method to generate an alert when client requests excessive amount of DNS queries to non-existent domains. This helps in identifying possible C2 communications. It utilizes ASIM normalization and is applied to any source that supports the ASIM DNS schema. | 
| Severity | Medium | 
| Tactics | CommandAndControl | 
| Techniques | T1568 T1008  | 
| Kind | Scheduled | 
| Query frequency | 1d | 
| Query period | 14d | 
| Trigger threshold | 0 | 
| Trigger operator | gt | 
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/DNS Essentials/Analytic Rules/ExcessiveNXDOMAINDNSQueriesAnomalyBased.yaml | 
| Version | 1.0.2 | 
| Arm template | 02f23312-1a33-4390-8b80-f7cd4df4dea0.json | 
let threshold = 2.5;
let min_t = ago(14d);
let max_t = now();
let dt = 1d;
let summarizationexist = (
  union isfuzzy=true 
      (
      DNS_Summarized_Logs_ip_CL
      | where EventTime_t > ago(1d) 
      | project v = int(2)
      ),
      (
      print int(1) 
      | project v = print_0
      )
  | summarize maxv = max(v)
  | extend sumexist = (maxv > 1)
  );
let allData = union isfuzzy=true
      (
      (datatable(exists: int, sumexist: bool)[1, false]
      | join (summarizationexist) on sumexist)
      | join (
          _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(min_t), endtime=max_t)
          | summarize Count=count() by SrcIpAddr, bin(TimeGenerated, 1h)
          | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)
          )
          on exists
      | project-away exists, maxv, sum*
      ),
      (
      DNS_Summarized_Logs_ip_CL
      | where EventTime_t > min_t and EventResultDetails_s == 'NXDOMAIN'
      | summarize Count=toint(sum(count__d)) by SrcIpAddr=SrcIpAddr_s, bin(EventTime=EventTime_t, 1h)
      );
allData
| make-series EventCount=sum(Count) on EventTime from min_t to max_t step dt by SrcIpAddr
| extend (anomalies, score, baseline) = series_decompose_anomalies(EventCount, threshold, -1, 'linefit')
| mv-expand anomalies, score, baseline, EventTime, EventCount
| extend
  anomalies = toint(anomalies),
  score = toint(score),
  baseline = toint(baseline),
  EventTime = todatetime(EventTime),
  Total = tolong(EventCount)
| where EventTime >= ago(dt)
| where score >= threshold * 2
| join kind=inner(_Im_Dns(responsecodename='NXDOMAIN', starttime=ago(dt), endtime=max_t)
  | summarize DNSQueries = make_set(DnsQuery) by SrcIpAddr)
  on SrcIpAddr
| project-away SrcIpAddr1
kind: Scheduled
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: "[Anomaly] Excessive NXDOMAIN DNS Queries has been detected from client IP: '{{SrcIpAddr}}'"
  alertDescriptionFormat: |-
    This client is generating excessive amount of DNS queries for non-existent domains. This can be an indication of possible C2 communications.
    Baseline for 'NXDOMAIN' error count for this client: '{{baseline}}'
    Current 'NXDOMAIN' error count for this client: '{{Total}}'
    DNS queries requested by the client include:
    '{{DNSQueries}}'    
entityMappings:
- entityType: IP
  fieldMappings:
  - columnName: SrcIpAddr
    identifier: Address
description: |
    'This rule makes use of the series decompose anomaly method to generate an alert when client requests excessive amount of DNS queries to non-existent domains. This helps in identifying possible C2 communications. It utilizes [ASIM](https://aka.ms/AboutASIM) normalization and is applied to any source that supports the ASIM DNS schema.'
severity: Medium
queryFrequency: 1d
customDetails:
  Total: Total
  baseline: baseline
  AnomalyScore: score
  DNSQueries: DNSQueries
triggerThreshold: 0
requiredDataConnectors: []
relevantTechniques:
- T1568
- T1008
status: Available
tactics:
- CommandAndControl
name: Detect excessive NXDOMAIN DNS queries - Anomaly based (ASIM DNS Solution)
id: 02f23312-1a33-4390-8b80-f7cd4df4dea0
query: |
  let threshold = 2.5;
  let min_t = ago(14d);
  let max_t = now();
  let dt = 1d;
  let summarizationexist = (
    union isfuzzy=true 
        (
        DNS_Summarized_Logs_ip_CL
        | where EventTime_t > ago(1d) 
        | project v = int(2)
        ),
        (
        print int(1) 
        | project v = print_0
        )
    | summarize maxv = max(v)
    | extend sumexist = (maxv > 1)
    );
  let allData = union isfuzzy=true
        (
        (datatable(exists: int, sumexist: bool)[1, false]
        | join (summarizationexist) on sumexist)
        | join (
            _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(min_t), endtime=max_t)
            | summarize Count=count() by SrcIpAddr, bin(TimeGenerated, 1h)
            | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)
            )
            on exists
        | project-away exists, maxv, sum*
        ),
        (
        DNS_Summarized_Logs_ip_CL
        | where EventTime_t > min_t and EventResultDetails_s == 'NXDOMAIN'
        | summarize Count=toint(sum(count__d)) by SrcIpAddr=SrcIpAddr_s, bin(EventTime=EventTime_t, 1h)
        );
  allData
  | make-series EventCount=sum(Count) on EventTime from min_t to max_t step dt by SrcIpAddr
  | extend (anomalies, score, baseline) = series_decompose_anomalies(EventCount, threshold, -1, 'linefit')
  | mv-expand anomalies, score, baseline, EventTime, EventCount
  | extend
    anomalies = toint(anomalies),
    score = toint(score),
    baseline = toint(baseline),
    EventTime = todatetime(EventTime),
    Total = tolong(EventCount)
  | where EventTime >= ago(dt)
  | where score >= threshold * 2
  | join kind=inner(_Im_Dns(responsecodename='NXDOMAIN', starttime=ago(dt), endtime=max_t)
    | summarize DNSQueries = make_set(DnsQuery) by SrcIpAddr)
    on SrcIpAddr
  | project-away SrcIpAddr1  
tags:
- Schema: ASimDns
  SchemaVersion: 0.1.6
version: 1.0.2
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/DNS Essentials/Analytic Rules/ExcessiveNXDOMAINDNSQueriesAnomalyBased.yaml
queryPeriod: 14d
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/02f23312-1a33-4390-8b80-f7cd4df4dea0')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/02f23312-1a33-4390-8b80-f7cd4df4dea0')]",
      "properties": {
        "alertDetailsOverride": {
          "alertDescriptionFormat": "This client is generating excessive amount of DNS queries for non-existent domains. This can be an indication of possible C2 communications.\n\nBaseline for 'NXDOMAIN' error count for this client: '{{baseline}}'\n\nCurrent 'NXDOMAIN' error count for this client: '{{Total}}'\n\nDNS queries requested by the client include:\n\n'{{DNSQueries}}'",
          "alertDisplayNameFormat": "[Anomaly] Excessive NXDOMAIN DNS Queries has been detected from client IP: '{{SrcIpAddr}}'"
        },
        "alertRuleTemplateName": "02f23312-1a33-4390-8b80-f7cd4df4dea0",
        "customDetails": {
          "AnomalyScore": "score",
          "baseline": "baseline",
          "DNSQueries": "DNSQueries",
          "Total": "Total"
        },
        "description": "'This rule makes use of the series decompose anomaly method to generate an alert when client requests excessive amount of DNS queries to non-existent domains. This helps in identifying possible C2 communications. It utilizes [ASIM](https://aka.ms/AboutASIM) normalization and is applied to any source that supports the ASIM DNS schema.'\n",
        "displayName": "Detect excessive NXDOMAIN DNS queries - Anomaly based (ASIM DNS Solution)",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "SrcIpAddr",
                "identifier": "Address"
              }
            ]
          }
        ],
        "eventGroupingSettings": {
          "aggregationKind": "AlertPerResult"
        },
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/DNS Essentials/Analytic Rules/ExcessiveNXDOMAINDNSQueriesAnomalyBased.yaml",
        "query": "let threshold = 2.5;\nlet min_t = ago(14d);\nlet max_t = now();\nlet dt = 1d;\nlet summarizationexist = (\n  union isfuzzy=true \n      (\n      DNS_Summarized_Logs_ip_CL\n      | where EventTime_t > ago(1d) \n      | project v = int(2)\n      ),\n      (\n      print int(1) \n      | project v = print_0\n      )\n  | summarize maxv = max(v)\n  | extend sumexist = (maxv > 1)\n  );\nlet allData = union isfuzzy=true\n      (\n      (datatable(exists: int, sumexist: bool)[1, false]\n      | join (summarizationexist) on sumexist)\n      | join (\n          _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(min_t), endtime=max_t)\n          | summarize Count=count() by SrcIpAddr, bin(TimeGenerated, 1h)\n          | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)\n          )\n          on exists\n      | project-away exists, maxv, sum*\n      ),\n      (\n      DNS_Summarized_Logs_ip_CL\n      | where EventTime_t > min_t and EventResultDetails_s == 'NXDOMAIN'\n      | summarize Count=toint(sum(count__d)) by SrcIpAddr=SrcIpAddr_s, bin(EventTime=EventTime_t, 1h)\n      );\nallData\n| make-series EventCount=sum(Count) on EventTime from min_t to max_t step dt by SrcIpAddr\n| extend (anomalies, score, baseline) = series_decompose_anomalies(EventCount, threshold, -1, 'linefit')\n| mv-expand anomalies, score, baseline, EventTime, EventCount\n| extend\n  anomalies = toint(anomalies),\n  score = toint(score),\n  baseline = toint(baseline),\n  EventTime = todatetime(EventTime),\n  Total = tolong(EventCount)\n| where EventTime >= ago(dt)\n| where score >= threshold * 2\n| join kind=inner(_Im_Dns(responsecodename='NXDOMAIN', starttime=ago(dt), endtime=max_t)\n  | summarize DNSQueries = make_set(DnsQuery) by SrcIpAddr)\n  on SrcIpAddr\n| project-away SrcIpAddr1\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CommandAndControl"
        ],
        "tags": [
          {
            "Schema": "ASimDns",
            "SchemaVersion": "0.1.6"
          }
        ],
        "techniques": [
          "T1008",
          "T1568"
        ],
        "templateVersion": "1.0.2",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}