Come rilevare il DNS tunneling su un server Linux
Ultimo aggiornamento:
Il DNS tunneling nasconde dati dentro le query DNS. Un programma sul server chiede nomi come mzxw6ytboi4dgnzt.tunnel.example, dove il sottodominio è il dato, e il name server dell'attaccante per tunnel.example risponde con altri dati in record TXT o di altro tipo. Funziona perché quasi ogni server può inviare query DNS, anche quando il resto del traffico in uscita è filtrato dal firewall. I malware lo usano per ricevere comandi e far uscire dati rubati; strumenti come iodine e dnscat2 ci costruiscono sopra intere connessioni di rete.
Questo traffico lascia tracce che il DNS normale non lascia: sottodomini lunghissimi e casuali, molti nomi unici sotto un solo dominio, una quota alta di query TXT, un ritmo costante di query verso un unico dominio. Qui sotto: come catturare e leggere il traffico DNS del server, controllare i log del resolver, trovare il programma e bloccare il tunnel. I comandi sono per Debian e Ubuntu e per la famiglia RHEL.
Riconoscere un tunnel DNS
| Segnale | Traffico normale | Un tunnel |
|---|---|---|
| Lunghezza del sottodominio | Parole brevi: www, mail, api | Da 30 a 63 caratteri casuali per etichetta, spesso più etichette |
| Nomi unici per dominio | Pochi, richiesti più e più volte | Centinaia o migliaia, ciascuno richiesto una volta |
| Tipi di query | Soprattutto A e AAAA | Molte query TXT, NULL, CNAME o MX |
| Ritmo | Picchi quando qualcuno usa un servizio | Un ritmo costante, giorno e notte, verso lo stesso dominio |
| Resolver | Quelli in /etc/resolv.conf | A volte un server DNS che il sistema non è configurato per usare |
Nota: Anche del traffico legittimo si comporta così: le ricerche di antivirus e servizi di reputazione (codificano gli hash dei file nei nomi), i controlli sulle blocklist di un server di posta e alcuni nomi di CDN e cloud. Controlla il dominio prima di parlare di tunnel.
Catturare il traffico DNS del server con tcpdump
Installa tcpdump, e tshark per l'analisi più avanti. Su Debian e Ubuntu l'installazione di tshark chiede se gli utenti non root possono catturare: rispondi no.
sudo apt install tcpdump tsharksudo dnf install tcpdump wireshark-cliRegistra cinque minuti di traffico DNS, UDP e TCP, su tutte le interfacce. Con systemd-resolved i programmi chiedono allo stub locale su 127.0.0.53 e lo stub chiede all'esterno: -i any vede entrambi.
sudo timeout 300 tcpdump -i any -nn -Z root -w /root/dns.pcap 'port 53'Poi leggilo. Prima la quota di ogni tipo di query: tanti TXT? o NULL? saltano all'occhio.
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 -rnPoi i domini con più nomi unici sotto di sé. Un tunnel porta in cima un solo dominio con centinaia di nomi; il traffico normale si distribuisce su molti domini con pochi nomi ciascuno. (Il conteggio raggruppa per le ultime due etichette, quindi example.co.uk compare come 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 -20E i nomi con un'etichetta di 30 caratteri o più:
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 -20Lo stesso con tshark
tshark dà le statistiche per tipo di query, l'origine e la destinazione di ogni query, e trova le risposte insolitamente grandi, che i tunnel usano per riportare indietro i dati:
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 | headNota: Così si vede solo il DNS sulla porta 53. Un tunnel su DNS over HTTPS viaggia come normale HTTPS sulla porta 443 e qui è invisibile; DNS over TLS usa la porta 853, che il passo 5 blocca.
Controllare i log del resolver
Guarda prima quali resolver il server è configurato per usare. Le query verso qualunque altro indirizzo meritano già da sole un'occhiata.
cat /etc/resolv.conf
resolvectl status 2>/dev/null | grep -E 'DNS Servers|Current DNS'systemd-resolved non tiene un log delle query, ma da systemd 252 (Debian 12, Ubuntu 24.04, RHEL 9 e successivi) mostra ogni ricerca in tempo reale. Lascialo girare qualche minuto e cerca nomi lunghi e casuali:
sudo resolvectl monitorSe il server ha un suo resolver, attivane il log delle query. Per unbound, poi 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 onEntrambi scrivono nel journal. Questo elenca i nomi con un'etichetta di 30 caratteri o più dell'ultima ora:
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 -20Attenzione: Su un resolver trafficato i log delle query crescono in fretta. Disattivali quando hai finito (log-queries: no, sudo rndc querylog off).
Trovare il programma dietro le query
I socket DNS vivono una frazione di secondo, quindi vanno campionati: questo guarda una volta al secondo per un minuto e conta i programmi che avevano un socket DNS aperto. I programmi che interrogano lo stub di systemd-resolved compaiono con il loro nome, perché anche lo stub ascolta sulla porta 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 -rnsystemd-resolve, unbound o named in cima vuol dire che le query passano dal resolver, da parte di un altro programma: ripeti la cattura solo sull'interfaccia di loopback (-i lo) e confronta gli orari. Per un programma che non conosci, guarda il binario, la cartella e l'unità che lo ha avviato:
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
systemctl status PIDUn client di tunnel è il segno di una compromissione, non la compromissione stessa. Cerca il resto come in Come trovare e rimuovere un crypto miner dal server: persistenza in cron, systemd e chiavi SSH, binari di sistema modificati, e come è entrato l'attaccante.
Bloccare il tunnel
Ferma prima il programma. Poi fai in modo che il DNS possa uscire solo dai resolver che hai scelto: questa tabella nftables scarta il DNS verso qualunque altro indirizzo, e il DNS over TLS ovunque. Sostituisci 192.0.2.53 con i tuoi resolver del passo 3 (più di uno, separati da virgole).
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 dropAttenzione: Con un indirizzo di resolver sbagliato il server non risolve più i nomi: aggiornamenti, posta e quasi tutte le applicazioni si fermano. Tieni aperta una seconda sessione, prova con getent hosts debian.org, e se fallisce rimuovi la tabella con sudo nft delete table inet dnsguard. Le regole coprono IPv4; aggiungi regole ip6 daddr se il server usa resolver IPv6. La tabella sparisce al riavvio.
Un tunnel attraverso il tuo resolver funziona ancora, perché il resolver inoltra le query. Se usi unbound, rispondi al dominio del tunnel con "non esiste":
# In the server: section of unbound.conf
local-zone: "tunnel.example." always_nxdomainsudo unbound-checkconf && sudo systemctl restart unboundCon il solo systemd-resolved non c'è un blocco per dominio: punta il server a un resolver che sappia farlo, o affidati alla rimozione del programma. /etc/hosts non aiuta, perché non può coprire ogni sottodominio.
Come InfraFence rileva il DNS tunneling
Con **Ispeziona il traffico DNS** attivo, che è l'impostazione predefinita, l'agent legge le query e le risposte DNS del server stesso a livello di pacchetto: porta UDP 53, in sola lettura, con un filtro che gira nel kernel. Ogni evento indica il programma che ha fatto la richiesta, anche quando è passata da systemd-resolved.
dns_tunneling: 60 o più sottodomini distinti di un dominio entro 5 minuti, lunghi in media (24 caratteri o più), contando i sottodomini di 16 caratteri o più e le query TXT e NULL. L'evento riporta il dominio, il numero di sottodomini e la loro lunghezza media.dns_dga: 20 o più domini dall'aspetto casuale e inesistenti entro 5 minuti, il segno di un malware che cerca il suo server.dns_threat_resolver(critico) quando una query va a un server DNS presente in una lista di minacce,dns_threat_answerquando un nome risolve a un indirizzo in lista, edns_unlisted_resolverquando un programma usa un server DNS diverso da quelli configurati.- Le ricerche di antivirus e reputazione, i controlli sulle blocklist della posta e le grandi CDN e cloud sono esclusi, e il servizio DNS del server stesso non viene contato come programma sospetto.
Vedi Minacce in uscita e DNS. Cosa non fa: segnala un tunnel e non lo blocca; i blocchi in uscita valgono solo per indirizzi nelle liste di minacce (Porte e blocco in uscita), quindi il blocco descritto sopra resta a te. Non vede il DNS su TCP, TLS o HTTPS, e un tunnel più lento delle soglie non viene segnalato.
Domande
- Il mio firewall blocca il traffico in uscita. Il DNS tunneling può funzionare lo stesso?
- Sì, se il server può interrogare un resolver, e quasi ogni server può. È il resolver a inoltrare le query al name server dell'attaccante. Per questo anche il resolver deve bloccare il dominio, oppure il programma deve sparire.
- DNSSEC o DNS over HTTPS lo fermano?
- No. DNSSEC dimostra che le risposte sono autentiche, e le risposte dell'attaccante sono autentiche per il suo dominio. DNS over HTTPS nasconde le query alla rete, il che rende un tunnel più difficile da vedere, non impossibile da costruire.
- Quale traffico legittimo somiglia a un tunnel?
- Le ricerche di antivirus e reputazione che codificano gli hash dei file nei nomi, i controlli sulle blocklist di un server di posta, e alcuni nomi di CDN, cloud e telemetria. Controlla il dominio prima di agire.
Provalo gratis su 1 server
Starter è gratuito per un server, con protezione completa e blocco automatico. Senza carta di credito.
Installa l'agent come root, con il tuo token al posto di <TOKEN>:
curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>Il token si crea nella dashboard, in Server › Aggiungi server.
