DNSPattern [Nobelium]


Description

This query looks for the DGA pattern of the domain associated with the Nobelium campaign, in order to find other domains with the same activity pattern. This query is inspired by an Azure Sentinel detection. Reference - https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Hunting%20Queries/DnsEvents/Solorigate-DNS-Pattern.yaml

Query · kql

let cloudApiTerms = dynamic(["api", "east", "west"]);
let timeFrame = ago(1d);
let relevantDeviceNetworkEvents = 
  DeviceNetworkEvents  
  | where Timestamp >= timeFrame
  | where RemoteUrl !has "\\" and RemoteUrl !has "/"
  // performance filter
  | where RemoteUrl has_any(cloudApiTerms)
  | project-rename DomainName = RemoteUrl
  | project Timestamp, DomainName, DeviceId, DeviceName;
let relevantDeviceEvents =   
  DeviceEvents 
  | where Timestamp >= timeFrame
   | where ActionType == "DnsQueryResponse"
   // performance filter
   | where AdditionalFields has_any(cloudApiTerms)
   | extend query = extractjson("$.DnsQueryString", AdditionalFields)  
   | where isnotempty(query)
   | project-rename DomainName = query
   | project Timestamp, DomainName, DeviceId, DeviceName;
let relevantIdentityQueryEvents =
  IdentityQueryEvents 
  | where Timestamp >= timeFrame
  | where ActionType == "DNS query"
  | where Protocol == "Dns"
  // performance filter
  | where QueryTarget has_any(cloudApiTerms)
  | project-rename DomainName = QueryTarget   
  | project Timestamp, DomainName, DeviceId = "", DeviceName;
let relevantData =
  relevantIdentityQueryEvents
  | union
  relevantDeviceNetworkEvents  
  | union
  relevantDeviceEvents;
let tokenCreation =
  relevantData
  | extend domain_split = split(DomainName, ".")
  | where tostring(domain_split[-5]) != "" and tostring(domain_split[-6]) == ""
  | extend sub_domain = tostring(domain_split[0])
  | where sub_domain !contains "-"
  | extend sub_directories = strcat(domain_split[-3], " ", domain_split[-4])
  | where sub_directories has_any(cloudApiTerms);
tokenCreation
  //Based on sample communications the subdomain is always between 20 and 30 bytes
  | where strlen(domain_split) < 32 or strlen(domain_split) > 20
  | extend domain = strcat(tostring(domain_split[-2]), ".", tostring(domain_split[-1])) 
  | extend subdomain_no = countof(sub_domain, @"(\d)", "regex")
  | extend subdomain_ch = countof(sub_domain, @"([a-z])", "regex")
  | where subdomain_no > 1
  | extend percentage_numerical = toreal(subdomain_no) / toreal(strlen(sub_domain)) * 100
  | where percentage_numerical < 50 and percentage_numerical > 5
  | summarize rowcount = count(), make_set(DomainName), make_set(DeviceId), make_set(DeviceName), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DomainName
  | order by rowcount asc
Raw source DNSPattern [Nobelium] · KQL
Esc
Published by Azure/Azure-Sentinel ↗, licensed under MIT ↗. Reproduced here unmodified.
id: 6c87bdb8-a44e-452a-b782-542640d985e3
name: DNSPattern [Nobelium]
description: |
  This query looks for the DGA pattern of the domain associated with the Nobelium campaign, in order to find other domains with the same activity pattern.
  This query is inspired by an Azure Sentinel detection.
  Reference - https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Hunting%20Queries/DnsEvents/Solorigate-DNS-Pattern.yaml
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
  dataTypes:
  - DeviceNetworkEvents
  - DeviceEvents
  - IdentityQueryEvents
tactics:
- Command and control
tags:
- Nobelium
query: |
  let cloudApiTerms = dynamic(["api", "east", "west"]);
  let timeFrame = ago(1d);
  let relevantDeviceNetworkEvents = 
    DeviceNetworkEvents  
    | where Timestamp >= timeFrame
    | where RemoteUrl !has "\\" and RemoteUrl !has "/"
    // performance filter
    | where RemoteUrl has_any(cloudApiTerms)
    | project-rename DomainName = RemoteUrl
    | project Timestamp, DomainName, DeviceId, DeviceName;
  let relevantDeviceEvents =   
    DeviceEvents 
    | where Timestamp >= timeFrame
     | where ActionType == "DnsQueryResponse"
     // performance filter
     | where AdditionalFields has_any(cloudApiTerms)
     | extend query = extractjson("$.DnsQueryString", AdditionalFields)  
     | where isnotempty(query)
     | project-rename DomainName = query
     | project Timestamp, DomainName, DeviceId, DeviceName;
  let relevantIdentityQueryEvents =
    IdentityQueryEvents 
    | where Timestamp >= timeFrame
    | where ActionType == "DNS query"
    | where Protocol == "Dns"
    // performance filter
    | where QueryTarget has_any(cloudApiTerms)
    | project-rename DomainName = QueryTarget   
    | project Timestamp, DomainName, DeviceId = "", DeviceName;
  let relevantData =
    relevantIdentityQueryEvents
    | union
    relevantDeviceNetworkEvents  
    | union
    relevantDeviceEvents;
  let tokenCreation =
    relevantData
    | extend domain_split = split(DomainName, ".")
    | where tostring(domain_split[-5]) != "" and tostring(domain_split[-6]) == ""
    | extend sub_domain = tostring(domain_split[0])
    | where sub_domain !contains "-"
    | extend sub_directories = strcat(domain_split[-3], " ", domain_split[-4])
    | where sub_directories has_any(cloudApiTerms);
  tokenCreation
    //Based on sample communications the subdomain is always between 20 and 30 bytes
    | where strlen(domain_split) < 32 or strlen(domain_split) > 20
    | extend domain = strcat(tostring(domain_split[-2]), ".", tostring(domain_split[-1])) 
    | extend subdomain_no = countof(sub_domain, @"(\d)", "regex")
    | extend subdomain_ch = countof(sub_domain, @"([a-z])", "regex")
    | where subdomain_no > 1
    | extend percentage_numerical = toreal(subdomain_no) / toreal(strlen(sub_domain)) * 100
    | where percentage_numerical < 50 and percentage_numerical > 5
    | summarize rowcount = count(), make_set(DomainName), make_set(DeviceId), make_set(DeviceName), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DomainName
    | order by rowcount asc

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.