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
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
| Sentinel Table | Notes |
|---|---|
DeviceNetworkEvents | Ensure this data connector is enabled |
Scenario: Legitimate DNS resolution for internal tools
Description: A system is resolving DNS for internal tools like vault.mycompany.com or jenkins.mycompany.com, which are legitimate but have low connection counts due to infrequent use.
Filter/Exclusion: Exclude hosts that are part of the internal domain (mycompany.com) using a filter like domain = mycompany.com or host like '%.mycompany.com'.
Scenario: Scheduled backups to a third-party storage service
Description: A backup job is connecting to a third-party storage provider (e.g., backup.provider.com) on a scheduled basis, which is a known and legitimate activity.
Filter/Exclusion: Exclude connections to known backup services using a filter like host like '%backup.%' or host like '%provider.com'.
Scenario: DNS lookups for internal DNSSEC validation
Description: DNSSEC validation processes may query rare DNS hosts as part of normal domain validation, leading to low-count entries.
Filter/Exclusion: Exclude DNS queries related to DNSSEC validation by checking for dnssec in the query metadata or using a filter like query_type = DNSSEC.
Scenario: Admin task to verify DNS records
Description: An admin is manually checking DNS records using a tool like nslookup or dig, which results in a single connection to a rare DNS host.
Filter/Exclusion: Exclude connections initiated by admin tools using a filter like process.name = nslookup or process.name = dig.
Scenario: Temporary DNS resolution for a one-off service
Description: A temporary service or script (e.g., a CI/CD pipeline job) resolves a rare DNS host once, which is part of a legitimate but infrequent operation.
*Filter/Ex