Detección de DNS Tunneling con KQL en Microsoft Sentinel
El DNS es el "listín telefónico" de internet y casi ningún firewall lo bloquea. Precisamente por eso los atacantes lo abusan: encapsulan datos dentro de consultas y respuestas DNS para exfiltrar información o mantener un canal de Command & Control (C2) que pasa desapercibido. En este artículo veremos cómo cazarlo en Microsoft Sentinel con KQL, sin comprar nada extra.
¿Por qué el DNS es un canal encubierto ideal?
- Rara vez se inspecciona a nivel de contenido; suele estar permitido de salida.
- Cada subdominio consultado puede transportar datos codificados (Base32/Base64/hex).
- Herramientas como
iodine,dnscat2oDNSExfiltratorlo automatizan.
La huella que deja es característica: subdominios largos y de alta entropía, un volumen anómalo de consultas hacia un mismo dominio y tipos de registro poco habituales (TXT, NULL, CNAME).
Señal 1 — Longitud y entropía del subdominio
Un dominio legítimo como cdn.contoso.com es corto y pronunciable. Un túnel produce algo como M FRGGZDF.k7x2.exfil.attacker.com. Podemos aproximar la entropía contando caracteres únicos frente a la longitud:
DnsEvents
| where TimeGenerated > ago(24h)
| where isnotempty(Name)
| extend sub = tostring(split(Name, ".")[0])
| extend subLen = strlen(sub)
| extend uniqueChars = array_length(set_difference(
extract_all(@"(.)", sub), dynamic([])))
| where subLen > 30 and uniqueChars > 15
| project TimeGenerated, ClientIP, Name, subLen, uniqueChars
| order by subLen desc
Ajusta los umbrales (subLen, uniqueChars) a tu entorno tras una fase de baseline.
Señal 2 — Volumen anómalo por dominio de segundo nivel
Un host que hace cientos de consultas únicas hacia el mismo dominio padre en poco tiempo es sospechoso: está "chorreando" datos subdominio a subdominio.
DnsEvents
| where TimeGenerated > ago(1h)
| extend parts = split(Name, ".")
| extend parentDomain = strcat(parts[array_length(parts)-2], ".", parts[array_length(parts)-1])
| summarize queries = count(), uniqueSub = dcount(Name) by ClientIP, parentDomain
| where uniqueSub > 200
| order by uniqueSub desc
Señal 3 — Tipos de registro poco comunes
El tráfico normal es mayoritariamente A/AAAA. Un pico de TXT o NULL desde un endpoint de usuario es una bandera roja:
DnsEvents
| where TimeGenerated > ago(24h)
| where QueryType in ("TXT", "NULL", "CNAME")
| summarize count() by ClientIP, QueryType, bin(TimeGenerated, 1h)
| where count_ > 50
De la detección a la respuesta
- Triaje: ¿el dominio padre es conocido? ¿el proceso que lanza las consultas es legítimo (correlaciona con
DeviceNetworkEventsde Defender)? - Contención: bloquea el dominio en el resolver/EDR y aísla el host si se confirma.
- Erradicación: identifica el binario responsable y su persistencia.
- Lecciones: convierte la caza en una regla analítica programada para no repetir el trabajo manual.
Si quieres practicar el lado ofensivo/forense de esto, en la plataforma tienes el reto OP: GHOST_TRAFFIC, donde analizas una captura con un túnel DNS real.