InfraFenceInfraFence

Live Security Operations

Inizia

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

SegnaleTraffico normaleUn tunnel
Lunghezza del sottodominioParole brevi: www, mail, apiDa 30 a 63 caratteri casuali per etichetta, spesso più etichette
Nomi unici per dominioPochi, richiesti più e più volteCentinaia o migliaia, ciascuno richiesto una volta
Tipi di querySoprattutto A e AAAAMolte query TXT, NULL, CNAME o MX
RitmoPicchi quando qualcuno usa un servizioUn ritmo costante, giorno e notte, verso lo stesso dominio
ResolverQuelli in /etc/resolv.confA 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.

Debian / Ubuntu
sudo apt install tcpdump tshark
RHEL, Rocky Linux, AlmaLinux, Oracle Linux, Fedora
sudo dnf install tcpdump wireshark-cli

Registra 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.

Qualsiasi distribuzione
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.

Qualsiasi distribuzione
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 -rn

Poi 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.)

Qualsiasi distribuzione
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 -20

E i nomi con un'etichetta di 30 caratteri o più:

Qualsiasi distribuzione
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 -20

Lo 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:

Qualsiasi distribuzione
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 | head

Nota: 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.

Qualsiasi distribuzione
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:

systemd-resolved
sudo resolvectl monitor

Se il server ha un suo resolver, attivane il log delle query. Per unbound, poi sudo systemctl restart unbound:

unbound
# /etc/unbound/unbound.conf (RHEL) or a file in /etc/unbound/unbound.conf.d/ (Debian, Ubuntu)
server:
    log-queries: yes
    log-replies: yes
BIND
sudo rndc querylog on

Entrambi scrivono nel journal. Questo elenca i nomi con un'etichetta di 30 caratteri o più dell'ultima ora:

Qualsiasi distribuzione
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 -20

Attenzione: 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.

Qualsiasi distribuzione
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 -rn

systemd-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:

Qualsiasi distribuzione
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
systemctl status PID

Un 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).

Qualsiasi distribuzione con nftables
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 drop

Attenzione: 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":

unbound
# In the server: section of unbound.conf
    local-zone: "tunnel.example." always_nxdomain
Qualsiasi distribuzione
sudo unbound-checkconf && sudo systemctl restart unbound

Con 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_answer quando un nome risolve a un indirizzo in lista, e dns_unlisted_resolver quando 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.