← Back to SOC feed Coverage →

Connection to Rare DNS Hosts

kql MEDIUM Azure-Sentinel
DeviceNetworkEvents
backdoorhuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-07T23:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may use rare DNS hosts to establish covert command and control channels, evading traditional detection methods. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential C2 activity and mitigate advanced threats.

KQL Query

let LowCountThreshold = 10;
let MaxAge = ago(1d);
DeviceNetworkEvents
| where Timestamp > MaxAge
| where isnotempty( RemoteUrl) and RemoteUrl contains "."
| extend RemoteDomain = iff(RemoteUrl matches regex @'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', tolower(RemoteUrl), tostring(parse_url(RemoteUrl).Host))
| top-nested 100000 of RemoteDomain by dcount(DeviceId) asc
| where aggregated_RemoteDomain <= LowCountThreshold 
| join kind=rightsemi (
    DeviceNetworkEvents
    | where Timestamp > ago(7d)
    | where isnotempty( RemoteUrl) and RemoteUrl contains "."
    | extend RemoteDomain = iff(RemoteUrl matches regex @'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', tolower(RemoteUrl), tostring(parse_url(RemoteUrl).Host))
) on RemoteDomain 
| extend DomainArray = split(RemoteDomain, '.')
| extend SecondLevelDomain = strcat(tostring(DomainArray[-2]),'.', tostring(DomainArray[-1])), ThirdLevelDomain = strcat(tostring(DomainArray[-3]), '.', tostring(DomainArray[-2]),'.', tostring(DomainArray[-1]))
| summarize ConnectionCount = count(), DistinctDevices = dcount(DeviceId) by SecondLevelDomain, ThirdLevelDomain, RemoteDomain
| where DistinctDevices <= LowCountThreshold 
| top 10000 by DistinctDevices asc
| order by ConnectionCount asc

Analytic Rule Definition

id: 402b16b9-b41d-477a-9e24-78fc1acdd051
name: Connection to Rare DNS Hosts
description: |
  This query will break down hostnames into their second and third level domain parts and analyze the volume of connections made to the destination to look for low count entries. Note that this query is likely to be rather noisy in many organziations and may benefit from analysis over time, anomaly detection, or perhaps machine learning.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - DeviceNetworkEvents
tactics:
- Command and control
query: |
  let LowCountThreshold = 10;
  let MaxAge = ago(1d);
  DeviceNetworkEvents
  | where Timestamp > MaxAge
  | where isnotempty( RemoteUrl) and RemoteUrl contains "."
  | extend RemoteDomain = iff(RemoteUrl matches regex @'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', tolower(RemoteUrl), tostring(parse_url(RemoteUrl).Host))
  | top-nested 100000 of RemoteDomain by dcount(DeviceId) asc
  | where aggregated_RemoteDomain <= LowCountThreshold 
  | join kind=rightsemi (
      DeviceNetworkEvents
      | where Timestamp > ago(7d)
      | where isnotempty( RemoteUrl) and RemoteUrl contains "."
      | extend RemoteDomain = iff(RemoteUrl matches regex @'^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$', tolower(RemoteUrl), tostring(parse_url(RemoteUrl).Host))
  ) on RemoteDomain 
  | extend DomainArray = split(RemoteDomain, '.')
  | extend SecondLevelDomain = strcat(tostring(DomainArray[-2]),'.', tostring(DomainArray[-1])), ThirdLevelDomain = strcat(tostring(DomainArray[-3]), '.', tostring(DomainArray[-2]),'.', tostring(DomainArray[-1]))
  | summarize ConnectionCount = count(), DistinctDevices = dcount(DeviceId) by SecondLevelDomain, ThirdLevelDomain, RemoteDomain
  | where DistinctDevices <= LowCountThreshold 
  | top 10000 by DistinctDevices asc
  | order by ConnectionCount asc

Required Data Sources

Sentinel TableNotes
DeviceNetworkEventsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/Microsoft 365 Defender/Command and Control/Connection to Rare DNS Hosts.yaml