Como detectar tunelamento DNS num servidor Linux
Última atualização:
O tunelamento DNS (DNS tunneling) esconde dados dentro de consultas DNS. Um programa no servidor pergunta por nomes como mzxw6ytboi4dgnzt.tunnel.example, em que o subdomínio é o dado, e o servidor de nomes do atacante para tunnel.example responde com mais dados em registros TXT ou de outros tipos. Funciona porque quase todo servidor pode enviar consultas DNS, até um cujo tráfego de saída esteja bloqueado no resto. Malwares usam isso para receber comandos e enviar dados roubados; ferramentas como iodine e dnscat2 montam conexões de rede inteiras por cima dele.
O tráfego deixa marcas que o DNS comum não deixa: subdomínios muito longos e aleatórios, muitos nomes únicos sob um mesmo domínio, uma proporção alta de consultas TXT, um ritmo constante de consultas a um único domínio. Abaixo: como capturar e ler o tráfego DNS do servidor, conferir os logs do resolvedor, achar o programa e bloquear o túnel. Os comandos são para Debian e Ubuntu e para a família RHEL.
Saiba como é um túnel DNS
| Sinal | Tráfego normal | Um túnel |
|---|---|---|
| Tamanho do subdomínio | Palavras curtas: www, mail, api | De 30 a 63 caracteres aleatórios por rótulo, muitas vezes vários rótulos |
| Nomes únicos por domínio | Poucos, consultados várias vezes | Centenas ou milhares, cada um consultado uma vez |
| Tipos de consulta | Principalmente A e AAAA | Muitas consultas TXT, NULL, CNAME ou MX |
| Ritmo | Picos quando alguém usa um serviço | Um ritmo constante, dia e noite, para o mesmo domínio |
| Resolvedor | Os que estão em /etc/resolv.conf | Às vezes um servidor DNS que o sistema não está configurado para usar |
Nota: Algum tráfego legítimo é parecido: consultas de antivírus e de reputação (elas codificam hashes de arquivos nos nomes), as consultas a blocklists que um servidor de e-mail faz e alguns nomes de CDNs e nuvens. Confira o domínio antes de chamá-lo de túnel.
Capture o tráfego DNS do servidor com tcpdump
Instale o tcpdump, e o tshark para a análise mais abaixo. No Debian e no Ubuntu, o instalador do tshark pergunta se usuários sem root podem capturar: responda não.
sudo apt install tcpdump tsharksudo dnf install tcpdump wireshark-cliGrave cinco minutos de tráfego DNS, UDP e TCP, em todas as interfaces. Com o systemd-resolved, os programas consultam o stub local em 127.0.0.53 e o stub consulta o servidor acima dele: -i any vê os dois.
sudo timeout 300 tcpdump -i any -nn -Z root -w /root/dns.pcap 'port 53'Depois leia a captura. Primeiro a proporção de cada tipo de consulta: muitos TXT? ou NULL? chamam a atenção.
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 -rnDepois os domínios com mais nomes únicos abaixo deles. Um túnel coloca um domínio no topo com centenas de nomes; o tráfego normal se espalha por muitos domínios com poucos nomes cada. (A contagem agrupa pelos dois últimos rótulos, então example.com.br aparece como com.br.)
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 os nomes com um rótulo de 30 caracteres ou mais:
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 -20O mesmo com o tshark
O tshark dá estatísticas por tipo de consulta e a origem e o destino de cada consulta, e encontra respostas grandes fora do comum, que os túneis usam para trazer dados de volta:
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: Isso só vê o DNS na porta 53. Um túnel sobre DNS over HTTPS trafega como HTTPS comum na porta 443 e é invisível aqui; o DNS over TLS usa a porta 853, que o passo 5 bloqueia.
Confira os logs do resolvedor
Primeiro veja quais resolvedores o servidor está configurado para usar. Consultas a qualquer outro endereço já merecem atenção por si só.
cat /etc/resolv.conf
resolvectl status 2>/dev/null | grep -E 'DNS Servers|Current DNS'O systemd-resolved não guarda log de consultas, mas a partir do systemd 252 (Debian 12, Ubuntu 24.04, RHEL 9 e posteriores) ele mostra cada consulta ao vivo. Deixe rodando por alguns minutos e procure nomes longos e aleatórios:
sudo resolvectl monitorSe o servidor roda um resolvedor próprio, ative o log de consultas dele. Para o unbound, depois rode 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 onOs dois registram no journal. Isto lista os nomes com um rótulo de 30 caracteres ou mais da última hora:
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 -20Atenção: Logs de consultas crescem rápido num resolvedor movimentado. Desative-os de novo quando terminar (log-queries: no, sudo rndc querylog off).
Encontre o programa por trás das consultas
Os sockets DNS duram uma fração de segundo, então faça amostras: isto olha uma vez por segundo durante um minuto e conta os programas que tinham um socket DNS aberto. Programas que consultam o stub do systemd-resolved aparecem com o próprio nome, já que o stub também escuta na 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 ou named no topo quer dizer que as consultas passam pelo resolvedor, vindas de outro programa: rode a captura de novo só na interface de loopback (-i lo) e compare os horários. Para um programa que você não conhece, veja o binário, a pasta e a unidade que o iniciou:
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
systemctl status PIDUm cliente de túnel é sinal de um comprometimento, não o comprometimento em si. Procure o resto como em Como encontrar e remover um minerador de criptomoedas: persistência no cron, no systemd e nas chaves SSH, binários do sistema alterados e como o invasor entrou.
Bloqueie o túnel
Pare o programa primeiro. Depois garanta que o DNS só possa sair pelos resolvedores que você escolheu: esta tabela do nftables descarta o DNS para qualquer outro endereço, e o DNS over TLS para todos. Troque 192.0.2.53 pelos seus resolvedores do passo 3 (vários, separados por vírgula).
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 dropAtenção: Com um endereço de resolvedor errado, o servidor não consegue mais resolver nomes: atualizações, e-mail e a maioria das aplicações param. Mantenha uma segunda sessão aberta, teste com getent hosts debian.org e, se falhar, remova a tabela com sudo nft delete table inet dnsguard. As regras cobrem IPv4; adicione regras ip6 daddr se o servidor usa resolvedores IPv6. A tabela some depois de um reboot.
Um túnel que passa pelo seu próprio resolvedor continua funcionando, porque o resolvedor encaminha as consultas. Se você roda o unbound, responda ao domínio do túnel com "não existe":
# In the server: section of unbound.conf
local-zone: "tunnel.example." always_nxdomainsudo unbound-checkconf && sudo systemctl restart unboundSó com o systemd-resolved não há bloqueio por domínio: aponte o servidor para um resolvedor que faça isso, ou conte com a remoção do programa. O /etc/hosts não ajuda, porque não consegue cobrir todos os subdomínios.
Como o InfraFence detecta tunelamento DNS
Com **Inspecionar o tráfego DNS** ativado, que é o padrão, o agente lê as consultas e respostas DNS do próprio servidor no nível de pacote: porta UDP 53, somente leitura, com um filtro que roda no kernel. Cada evento informa o programa que fez a consulta, também quando ela passou pelo systemd-resolved.
dns_tunneling: 60 ou mais subdomínios distintos de um domínio em 5 minutos, longos na média (24 caracteres ou mais), contando subdomínios de 16 caracteres ou mais e consultas TXT e NULL. O evento traz o domínio, o número de subdomínios e o tamanho médio deles.dns_dga: 20 ou mais domínios de aparência aleatória que não existem, em 5 minutos, a marca de um malware procurando o seu servidor.dns_threat_resolver(crítico) quando uma consulta vai para um servidor DNS numa lista de ameaças,dns_threat_answerquando um nome resolve para um endereço listado, edns_unlisted_resolverquando um programa usa um servidor DNS diferente dos configurados.- Consultas de antivírus e de reputação, verificações de blocklists de e-mail e grandes CDNs e nuvens ficam de fora, e o serviço DNS do próprio servidor não conta como programa suspeito.
Veja Ameaças de saída e DNS. O que ele não faz: reporta um túnel e não o bloqueia; os bloqueios de saída valem só para endereços em listas de ameaças (Portas e bloqueio de saída), então o bloqueio acima continua com você. Ele não vê DNS sobre TCP, TLS ou HTTPS, e um túnel mais lento que os limites não é reportado.
Perguntas
- Meu firewall bloqueia o tráfego de saída. O tunelamento DNS ainda funciona?
- Sim, se o servidor pode consultar um resolvedor, e quase todo servidor pode. O resolvedor encaminha as consultas ao servidor de nomes do atacante por você. Por isso o resolvedor também precisa bloquear o domínio, ou o programa precisa sair.
- DNSSEC ou DNS over HTTPS impedem isso?
- Não. O DNSSEC prova que as respostas são autênticas, e as respostas do atacante são autênticas para o próprio domínio dele. O DNS over HTTPS esconde as consultas da rede, o que deixa um túnel mais difícil de ver, não impossível de montar.
- Que tráfego legítimo parece um túnel?
- Consultas de antivírus e de reputação que codificam hashes de arquivos nos nomes, as consultas a blocklists de um servidor de e-mail e alguns nomes de CDN, nuvem e telemetria. Pesquise o domínio antes de agir.
Teste grátis em 1 servidor
O Starter é gratuito para um servidor, com proteção completa e bloqueio automático. Sem cartão de crédito.
Instale o agente como root, com o seu token no lugar de <TOKEN>:
curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>O token é criado no painel, em Servidores › Adicionar servidor.
