InfraFenceInfraFence

Live Security Operations

Começar

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

SinalTráfego normalUm túnel
Tamanho do subdomínioPalavras curtas: www, mail, apiDe 30 a 63 caracteres aleatórios por rótulo, muitas vezes vários rótulos
Nomes únicos por domínioPoucos, consultados várias vezesCentenas ou milhares, cada um consultado uma vez
Tipos de consultaPrincipalmente A e AAAAMuitas consultas TXT, NULL, CNAME ou MX
RitmoPicos quando alguém usa um serviçoUm ritmo constante, dia e noite, para o mesmo domínio
ResolvedorOs 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.

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

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

Qualquer distribuição
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.

Qualquer distribuiçã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 -rn

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

Qualquer distribuição
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 os nomes com um rótulo de 30 caracteres ou mais:

Qualquer distribuição
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

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

Qualquer distribuição
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: 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ó.

Qualquer distribuição
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:

systemd-resolved
sudo resolvectl monitor

Se o servidor roda um resolvedor próprio, ative o log de consultas dele. Para o unbound, depois rode 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

Os dois registram no journal. Isto lista os nomes com um rótulo de 30 caracteres ou mais da última hora:

Qualquer distribuição
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

Atençã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.

Qualquer distribuição
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 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:

Qualquer distribuição
sudo ls -l /proc/PID/exe /proc/PID/cwd
sudo tr '\0' ' ' < /proc/PID/cmdline; echo
systemctl status PID

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

Qualquer distribuição com 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

Atençã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":

unbound
# In the server: section of unbound.conf
    local-zone: "tunnel.example." always_nxdomain
Qualquer distribuição
sudo unbound-checkconf && sudo systemctl restart unbound

Só 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_answer quando um nome resolve para um endereço listado, e dns_unlisted_resolver quando 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.