Comment détecter le tunneling DNS sur un serveur Linux
Dernière mise à jour:
Le tunneling DNS cache des données dans des requêtes DNS. Un programme du serveur demande des noms comme mzxw6ytboi4dgnzt.tunnel.example, où le sous-domaine est la donnée, et le serveur de noms de l'attaquant pour tunnel.example répond avec d'autres données dans des enregistrements TXT ou autres. Cela fonctionne parce que presque tous les serveurs ont le droit d'envoyer des requêtes DNS, même ceux dont le trafic sortant est par ailleurs filtré. Les malwares s'en servent pour recevoir des commandes et faire sortir des données volées ; des outils comme iodine et dnscat2 construisent des connexions réseau entières par ce canal.
Ce trafic laisse des traces que le DNS ordinaire ne laisse pas : des sous-domaines très longs et aléatoires, de nombreux noms uniques sous un même domaine, une forte part de requêtes TXT, un rythme régulier de requêtes vers un seul domaine. Ci-dessous : comment capturer et lire le trafic DNS du serveur, vérifier les logs du résolveur, trouver le programme et bloquer le tunnel. Les commandes sont pour Debian et Ubuntu et pour la famille RHEL.
Savoir à quoi ressemble un tunnel DNS
| Signe | Trafic normal | Un tunnel |
|---|---|---|
| Longueur des sous-domaines | Des mots courts : www, mail, api | 30 à 63 caractères aléatoires par label, souvent plusieurs labels |
| Noms uniques par domaine | Quelques-uns, demandés encore et encore | Des centaines ou des milliers, chacun demandé une fois |
| Types de requêtes | Surtout A et AAAA | Beaucoup de requêtes TXT, NULL, CNAME ou MX |
| Rythme | Des pics quand quelqu'un utilise un service | Un débit régulier, jour et nuit, vers le même domaine |
| Résolveur | Ceux de /etc/resolv.conf | Parfois un serveur DNS que le système n'est pas configuré pour utiliser |
Remarque: Certains trafics légitimes y ressemblent : les recherches des antivirus et des services de réputation (qui encodent des empreintes de fichiers dans les noms), les vérifications de listes noires d'un serveur de messagerie, et certains noms de CDN et de clouds. Vérifiez le domaine avant de parler de tunnel.
Capturer le trafic DNS du serveur avec tcpdump
Installez tcpdump, et tshark pour l'analyse plus bas. Sur Debian et Ubuntu, l'installation de tshark demande si les utilisateurs non root peuvent capturer : répondez non.
sudo apt install tcpdump tsharksudo dnf install tcpdump wireshark-cliEnregistrez cinq minutes de trafic DNS, UDP et TCP, sur toutes les interfaces. Avec systemd-resolved, les programmes interrogent le stub local sur 127.0.0.53 et le stub interroge l'amont : -i any voit les deux.
sudo timeout 300 tcpdump -i any -nn -Z root -w /root/dns.pcap 'port 53'Lisez-le ensuite. D'abord la part de chaque type de requête : beaucoup de TXT? ou de NULL? saute aux yeux.
sudo tcpdump -nn -r /root/dns.pcap 'dst port 53' 2>/dev/null \
| grep -oE '[A-Z0-9]+\? [^ ]+' | awk '{print $1}' | sort | uniq -c | sort -rnPuis les domaines qui ont le plus de noms uniques. Un tunnel place un domaine en tête avec des centaines de noms ; le trafic normal se répartit sur de nombreux domaines avec peu de noms chacun. (Le décompte regroupe par les deux derniers labels : example.co.uk apparaît comme co.uk.)
sudo tcpdump -nn -r /root/dns.pcap 'dst port 53' 2>/dev/null \
| grep -oE '[A-Z0-9]+\? [^ ]+' | awk '{print tolower($2)}' | sort -u \
| awk -F. '{n = NF - 1; if (n >= 2) print $(n-1) "." $n}' | sort | uniq -c | sort -rn | head -20Et les noms qui ont un label de 30 caractères ou plus :
sudo tcpdump -nn -r /root/dns.pcap 'dst port 53' 2>/dev/null \
| grep -oE '[A-Z0-9]+\? [^ ]+' | awk '{print $2}' \
| awk -F. '{for (i = 1; i < NF; i++) if (length($i) >= 30) {print; break}}' | sort -u | head -20La même chose avec tshark
tshark donne des statistiques par type de requête, la source et la destination de chaque requête, et trouve les réponses anormalement grandes, que les tunnels utilisent pour faire revenir des données :
sudo tshark -r /root/dns.pcap -q -z dns,tree
sudo tshark -r /root/dns.pcap -Y 'dns.flags.response == 0' -T fields \
-e ip.src -e ip.dst -e dns.qry.type -e dns.qry.name | head -50
sudo tshark -r /root/dns.pcap -Y 'dns.flags.response == 1 && udp.length > 512' -T fields \
-e dns.qry.name | sort | uniq -c | sort -rn | headRemarque: Ceci ne voit que le DNS sur le port 53. Un tunnel sur DNS over HTTPS circule comme du HTTPS ordinaire sur le port 443 et reste invisible ici ; DNS over TLS utilise le port 853, que l'étape 5 bloque.
Vérifier les logs du résolveur
Regardez d'abord quels résolveurs le serveur est configuré pour utiliser. Des requêtes vers toute autre adresse méritent à elles seules un examen.
cat /etc/resolv.conf
resolvectl status 2>/dev/null | grep -E 'DNS Servers|Current DNS'systemd-resolved ne garde pas de log des requêtes, mais depuis systemd 252 (Debian 12, Ubuntu 24.04, RHEL 9 et ultérieurs) il affiche chaque résolution en direct. Laissez-le tourner quelques minutes et guettez les noms longs et aléatoires :
sudo resolvectl monitorSi le serveur fait tourner son propre résolveur, activez son log des requêtes. Pour unbound, puis sudo systemctl restart unbound :
# /etc/unbound/unbound.conf (RHEL) or a file in /etc/unbound/unbound.conf.d/ (Debian, Ubuntu)
server:
log-queries: yes
log-replies: yessudo rndc querylog onLes deux écrivent dans le journal. Cette commande liste les noms qui ont un label de 30 caractères ou plus sur la dernière heure :
sudo journalctl --since "1 hour ago" -u unbound -u named -u bind9 \
| grep -ioE '([a-z0-9-]{30,}\.)+[a-z0-9.-]+' | sort | uniq -c | sort -rn | head -20Attention: Les logs de requêtes grossissent vite sur un résolveur chargé. Désactivez-les une fois terminé (log-queries: no, sudo rndc querylog off).
Trouver le programme derrière les requêtes
Les sockets DNS vivent une fraction de seconde : il faut donc échantillonner. Cette commande regarde une fois par seconde pendant une minute et compte les programmes qui avaient un socket DNS ouvert. Les programmes qui interrogent le stub de systemd-resolved apparaissent sous leur propre nom, puisque le stub écoute lui aussi sur le port 53.
for i in $(seq 1 60); do sudo ss -unpH '( dport = :53 )'; sleep 1; done \
| grep -o 'users:(("[^"]*",pid=[0-9]*' | sort | uniq -c | sort -rnSi systemd-resolve, unbound ou named arrive en tête, les requêtes passent par le résolveur et viennent d'un autre programme : relancez la capture sur la seule interface loopback (-i lo) et comparez les heures. Pour un programme que vous ne connaissez pas, regardez son binaire, son dossier et l'unité qui l'a lancé :
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
systemctl status PIDUn client de tunnel est un signe de compromission, pas la compromission elle-même. Cherchez le reste comme dans Comment trouver et supprimer un crypto-mineur sur votre serveur : persistance dans cron, systemd et les clés SSH, binaires système modifiés, et la façon dont l'attaquant est entré.
Bloquer le tunnel
Arrêtez d'abord le programme. Faites ensuite en sorte que le DNS ne puisse sortir que par les résolveurs que vous avez choisis : cette table nftables rejette le DNS vers toute autre adresse, et le DNS over TLS partout. Remplacez 192.0.2.53 par vos résolveurs de l'étape 3 (plusieurs, séparés par des virgules).
sudo nft add table inet dnsguard
sudo nft 'add chain inet dnsguard out { type filter hook output priority 0; policy accept; }'
sudo nft add rule inet dnsguard out ip daddr != { 127.0.0.0/8, 192.0.2.53 } meta l4proto { tcp, udp } th dport 53 drop
sudo nft add rule inet dnsguard out tcp dport 853 dropAttention: Avec une mauvaise adresse de résolveur, le serveur ne résout plus aucun nom : mises à jour, courrier et la plupart des applications s'arrêtent. Gardez une deuxième session ouverte, testez avec getent hosts debian.org et, en cas d'échec, supprimez la table avec sudo nft delete table inet dnsguard. Les règles couvrent IPv4 ; ajoutez des règles ip6 daddr si le serveur utilise des résolveurs IPv6. La table disparaît au redémarrage.
Un tunnel qui passe par votre propre résolveur fonctionne encore, puisque le résolveur transmet les requêtes. Si vous utilisez unbound, répondez « n'existe pas » pour le domaine du tunnel :
# In the server: section of unbound.conf
local-zone: "tunnel.example." always_nxdomainsudo unbound-checkconf && sudo systemctl restart unboundAvec systemd-resolved seul, il n'y a pas de blocage par domaine : faites pointer le serveur vers un résolveur qui en est capable, ou comptez sur la suppression du programme. /etc/hosts n'aide pas, car il ne peut pas couvrir tous les sous-domaines.
Comment InfraFence détecte le tunneling DNS
Avec **Inspecter le trafic DNS** activé, ce qui est le réglage par défaut, l'agent lit les requêtes et réponses DNS propres au serveur au niveau des paquets : port UDP 53, en lecture seule, avec un filtre qui s'exécute dans le noyau. Chaque événement indique le programme qui a posé la question, y compris quand elle est passée par systemd-resolved.
dns_tunneling: 60 sous-domaines distincts ou plus d'un même domaine en 5 minutes, longs en moyenne (24 caractères ou plus), en comptant les sous-domaines de 16 caractères ou plus et les requêtes TXT et NULL. L'événement donne le domaine, le nombre de sous-domaines et leur longueur moyenne.dns_dga: 20 domaines inexistants ou plus d'aspect aléatoire en 5 minutes, la marque d'un malware qui cherche son serveur.dns_threat_resolver(critique) quand une requête part vers un serveur DNS présent sur une liste de menaces,dns_threat_answerquand un nom résout vers une adresse listée, etdns_unlisted_resolverquand un programme utilise un serveur DNS autre que ceux configurés.- Les recherches des antivirus et des services de réputation, les vérifications de listes noires du courrier et les grands CDN et clouds sont écartés, et le service DNS propre au serveur n'est pas compté comme un programme suspect.
Voir Menaces sortantes et DNS. Ce qu'il ne fait pas : il signale un tunnel et ne le bloque pas ; les blocages sortants ne s'appliquent qu'aux adresses des listes de menaces (Ports et blocage sortant), le blocage ci-dessus reste donc à votre charge. Il ne voit pas le DNS sur TCP, TLS ou HTTPS, et un tunnel plus lent que les seuils n'est pas signalé.
Questions
- Mon pare-feu bloque le trafic sortant. Le tunneling DNS peut-il quand même fonctionner ?
- Oui, si le serveur peut interroger un résolveur, ce qui est le cas de presque tous. Le résolveur transmet les requêtes au serveur de noms de l'attaquant à votre place. C'est pourquoi le résolveur doit aussi bloquer le domaine, ou le programme doit disparaître.
- DNSSEC ou DNS over HTTPS l'empêchent-ils ?
- Non. DNSSEC prouve que les réponses sont authentiques, et les réponses de l'attaquant sont authentiques pour son propre domaine. DNS over HTTPS cache les requêtes au réseau, ce qui rend un tunnel plus difficile à voir, pas impossible à construire.
- Quel trafic légitime ressemble à un tunnel ?
- Les recherches des antivirus et des services de réputation qui encodent des empreintes de fichiers dans les noms, les vérifications de listes noires d'un serveur de messagerie, et certains noms de CDN, de clouds et de télémétrie. Renseignez-vous sur le domaine avant d'agir.
Essayez gratuitement sur 1 serveur
Starter est gratuit pour un serveur, avec la protection complète et le blocage automatique. Sans carte bancaire.
Installez l'agent en root, avec votre jeton à la place de <TOKEN> :
curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>Le jeton se crée dans le tableau de bord, dans Serveurs › Ajouter un serveur.
