bind: blackhole para consultas recursivas inválidas?

13

Eu tenho um servidor de nomes acessível ao público, pois é o servidor de nomes oficial para alguns domínios .

Atualmente, o servidor é inundado por ANYsolicitações de tipo falsificado para isc.org, mature.net e similares (que é um ataque de DoS distribuído conhecido ).

O servidor executa o BIND e allow-recursionconfigurou a minha LAN para que essas solicitações sejam rejeitadas. Nesses casos, o servidor responde apenas com authoritye additionalseções referentes aos servidores raiz.

Posso configurar o BIND para que ele ignore completamente essas solicitações, sem enviar uma resposta?

Udo G
fonte

Respostas:

5

Diante do mesmo problema, optei por ignorar todos os pedidos recursivos. Todos os resolvedores enviam uma consulta não recursiva quando desejam usar meu servidor como um servidor autoritativo. Somente clientes e atacantes mal configurados, no meu próprio caso, usam consultas recursivas.

Infelizmente, não encontrei uma maneira de deixar o BIND fazer isso, mas, caso o iptables seja bom o suficiente para você, usei

iptables -t raw -I PREROUTING -i eth0 -p udp --destination-port 53 \
    -m string --algo kmp --from 30 \
    --hex-string "|01000001000000000000|" -j DROP
pino42
fonte
Não, essa regra também bloqueia solicitações do tipo autoritativas (pelo menos na minha máquina). Aparentemente, ele bloqueia todos os tipos de solicitações de DNS.
Udo G
Verifiquei novamente e estou usando exatamente essa regra. Aqui está um recortar e colar do servidor ativo. Comando: iptables -t raw -S PREROUTING. Saída:, -P PREROUTING ACCEPTseguida por -A PREROUTING -i eth0 -p udp -m udp --dport 53 -m string --hex-string "|01000001000000000000|" --algo kmp --from 30 --to 65535 -j DROP. Eu testei que ele está funcionando corretamente host -ar exampledomain.com dns-server.example.net. Claro que não funcionou corretamente até eu adicionar a -ropção.
Pino42
Ok, a -ropção faz a diferença. Pessoalmente, não gosto que hostconsultas simples não funcionem mais e isso pode ser muito confuso. Essa provavelmente é uma resposta válida (a melhor até agora) e eu darei a você a recompensa, já que ela está prestes a expirar, mesmo que eu continue usando a minha própria abordagem filtrando OUTPUT.
Udo G
Obrigado! Se eu encontrar uma solução melhor, não deixarei de publicá-la. Eu concordo com você: este é um truque. Um trabalho, mas ainda um hack.
Pino42
2

Eu tentaria:

zone "." {
  type redirect;
  allow-query "none";
}

As respostas referentes aos clientes para os servidores raiz são controladas pela zona "redirecionar". Isso deve dizer para não responder a eles.

Isso é sugerido nos documentos do Bind9: http://ftp.isc.org/isc/bind9/cur/9.9/doc/arm/Bv9ARM.ch06.html#id2592674

Você pode substituir "none" com sua sub-rede local.

Se você já possui uma zone "."declaração, basta adicioná allow-query "none";-la.

freiheit
fonte
Eu tenho uma zone "." { type hint; file "/etc/bind/db.root"; };declaração com o db.root listando os servidores raiz. A remoção dessa declaração interrompe as respostas para domínios estrangeiros, mas o servidor ainda está respondendo com uma "falha no servidor" e, portanto, ainda pode ser usado para o DoS.
Udo G
@UdoG: Você já tentou adicionar allow-query "none";à zone "."configuração?
Freiheit
Parece que isso está economizando apenas largura de banda upstream, o que deve ser abundante. Com a sua correção o atacante já tenha utilizado seus servidores de largura de banda downstream e poder de processamento
TheLQ
@ TheLQ: A pergunta refere-se a este ataque DDoS. O ataque DDoS comum baseado em DNS é enviar consultas DNS com o IP forjado do destino. Como o pacote de resposta DNS é maior que a consulta, ele fornece um multiplicador. Se o servidor não responder com um pacote significativamente maior, você eliminou o motivo pelo qual ele está usando o servidor no ataque.
002 freiheit #
@UdoG: O pacote de falha do servidor é de apenas 31 a 32, enquanto a referência aos servidores raiz era provavelmente de várias centenas de bytes. Se a resposta do servidor for do mesmo tamanho da consulta ou apenas um pouquinho maior, seu servidor será inútil em um ataque DDoS DNS, pois os invasores consumirão tanta largura de banda quanto o envio para o destino. Eu testei em relação a vários prováveis ​​servidores de nomes autoritativos bem configurados (como o Google) e eles responderam com "recursão solicitada, mas não disponível".
002 freiheit #
1

Geralmente, eu sugeriria:

Ative os logs de ligação e registre os ips que recebem resposta rejeitada. Instale o programa fail2ban, adicione a ação blackhole: http://pastebin.com/k4BxrAeG (coloque a regra no arquivo em /etc/fail2ban/actions.d)

Crie um arquivo de filtro de ligação em /etc/fail2ban/filter.d com algo parecido com isto (precisa de depuração!)

[Definition]
failregex = ^.* security: info: client #<HOST>: query \(cache\) .* denied

Edite fail2ban.conf, adicione a seção:

[bindban]

enabled  = true
filter   = bind
# "bantime" is the number of seconds that a host is banned.
bantime  = 6000
# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime  = 60
# "maxretry" is the number of failures before a host get banned.
maxretry = 150
action   = blackhole
logpath  = /var/log/named.log

Espero que isso ajude!

Andrei Mikhaltsov
fonte
TODO: exemplo de arquivo de log de ligação.
Andrei Mikhaltsov 02/11/2012
1

A idéia básica permite que o bind classifique a resposta DNS como recusada e use o iptables para converter a recusada em ignorada silenciosamente.

Recusado é a parte fácil da seção de opções named.conf:

allow-recursion { none;};

Ou, é claro, suas ACLs favoritas para exceções locais ...

Na próxima mágica do iptables, ajuste ou remova "-o eth0", conforme necessário. Este comando assume o cabeçalho padrão da camada IPv4 de 20 bytes antes do UDP.

iptables -A OUTPUT -o eth0 -p udp --sport 53 -m string --from 30 --to 32 --hex-string "|8105|" --algo bm -j DROP

Essas teclas no campo sinalizadores da resposta DNS com os seguintes bits definidos

  • Resposta DNS
  • Consulta recursiva
  • Código de resposta recusado

A mensagem de log percebida executando a ligação na depuração "erro ao enviar resposta: host inacessível" quando a regra corresponde a ter algum feedback para o teste.

Devo admitir que tudo isso é um exercício um tanto inútil. Se não houver amplificação, um invasor poderá refletir com a mesma facilidade o TCP SYN. Em última análise, o DNS está quebrado simplesmente não há solução viável além do uso do TCP ou da implantação dos cookies DNS da Eastlake.

comedor de disco
fonte
0

Você tentou bloquear a string isc.org ou bloquear a string hexadecimal?

Isso funcionou para mim:

iptables -A INPUT -p udp -m string --hex-string "| 03697363036f726700 |" --algo bm -j DROP

Hex
fonte
Não seria melhor identificar as seqüências hexadecimais para todos os domínios aos quais o servidor deveria estar respondendo, fazer o acima para permitir isso e eliminar todos os outros tráfegos do udp / 53?
28512 freiheit
Atualmente, já estou bloqueando respostas UDP que se referem aos servidores raiz: iptables -A OUTPUT -p udp -m string -hex-string "|726f6f742d73657276657273|" –algo bm –to 65535 -j DROPmas eu realmente preferiria uma solução baseada apenas na configuração do BIND, se isso for possível.
Udo G
isso é fraco. você pode gerar o que quiser como domínio. estamos enfrentando esse problema agora e não é possível bloqueá-lo por um nome estático #'bnrexex.www.sf97.net/A/IN' 'whzpkacpxpiuycm.www.tpa.net.cn/A/IN'
3h4x
0

Esse ataque é chamado de negação de serviço amplificada. Você deve configurar a ligação corretamente, mas esse tráfego não deve chegar à sua ligação em primeiro lugar. Bloqueie-o no primeiro dispositivo de rede capaz de fazê-lo na sua rede. Eu tive o mesmo problema e lidei com isso com a regra do ronco surdo:

alert udp $ EXTERNAL_NET any -> $ HOME_NET 53 (msg: "consultas excessivas em PROTOCOLO-DNS do tipo ANY - DoS em potencial"; byte_test: 1,! &, 0xF8,2; conteúdo: "| 00 00 FF 00 01 |"; detection_filter: track by_src, count 30, seconds 30; metadata: service dns; reference: url, foxpa.ws / 2010/07/21 / thwarting-isc-org-dns-ddos /; classtype: tentativa-dos; sid : 21817; rev: 4;)

3h4x
fonte
0

Primeiro, eu sei que essa é uma pergunta antiga, mas ...

Estou executando meu próprio servidor DNS autoritativo e não recursivo há décadas, mas nunca fui vítima de nenhum ataque DDoS baseado em DNS - até agora, quando mudei para um novo ISP. Milhares de consultas DNS falsificadas inundaram meus logs e fiquei muito irritado - não tanto pelo impacto no meu servidor, mas pelo fato de ele atrapalhar meus logs e pela sensação desconfortável de ser abusado. Parece que o invasor tenta usar meu DNS em um " ataque do servidor de nomes autoritário ".

Então, imaginei que, embora eu limite consultas recursivas à minha rede interna (negando todas as outras), prefiro gastar meus ciclos de CPU na correspondência de cadeias de caracteres em tabelas de ip do que enviando respostas negativas aos endereços IP falsificados (menos confusão nos meus logs, menos tráfego de rede e um nível mais alto de satisfação).

Comecei fazendo o que todo mundo parece fazer , descubra quais nomes de domínio são consultados e criei uma correspondência de string nesse domínio com uma DROP de destino. Mas logo percebi que acabaria com uma enorme quantidade de regras, cada uma delas consumindo ciclos de CPU. Então o que fazer? Como não executo um servidor de nomes recursivo, achei que poderia fazer a correspondência nas zonas reais para as quais sou autoritário e largar todo o resto.

Minha política padrão no iptables é ACEITAR, se sua política for DROP, você provavelmente precisará fazer alguns ajustes se desejar usar a seguinte solução.

Eu mantenho minha configuração de zona em um arquivo separado (/etc/bind/named.conf.local), vamos usar isso como um exemplo:

zone "1.168.192.in-addr.arpa" { // Private
        type master;
        allow-query { 192.168.1.0/24; 127.0.0.1; };
        allow-transfer { 127.0.0.1; };
        file "/etc/bind/db.192.168.1";
};

zone "home.example.net" { // Private
        type master;
        allow-query { 192.168.1.0/24; 127.0.0.1; };
        allow-transfer { 127.0.0.1; };
        file "/etc/bind/pri/db.home.example.net";
};

zone "example.net" {
        type master;
        file "/etc/bind/pri/db.example.net";
        allow-transfer { 127.0.0.1; 8.8.8.8; };
};

zone "example.com" {
        type slave;
        masters { 8.8.8.8; };
        file "sec.example.com";
        allow-transfer { 127.0.0.1; };
        notify no;
};

zone "subdomain.of.example.nu" {
        type slave;
        masters { 8.8.8.8; };
        file "sec.subdomain.of.example.nu";
        allow-transfer { 127.0.0.1; };
        notify no;
};

Observe o comentário “// Privado” nas minhas duas primeiras zonas, eu uso isso no script a seguir para excluí-las da lista de zonas válidas.

#!/usr/bin/perl
# zone2iptables - Richard Lithvall, april 2014
#
# Since we want to match not only example.net, but also (for example)
# www.example.net we need to set a reasonable maximum value for a domain
# name in our zones - 100 character should be more that enough for most people
# and 255 is the absolute maximum allowed in rfc1034.
# Set it to 0 (zero) if you would like the script to fetch each zone (axfr)
# to get the actual max value.
$maxLengthOfQueryName=255;
$externalInterface="eth1";

print "# first time you run this, you will get error on the 3 first commands.\n";
print "# It's here to make it safe/possible to periodically run this script.\n";
print "/sbin/iptables -D INPUT -i $externalInterface -p udp --dport 53 -j DNSvalidate\n";
print "/sbin/iptables -F DNSvalidate\n";
print "/sbin/iptables -X DNSvalidate\n";
print "#\n";
print "# now, create the chain (again)\n";
print "/sbin/iptables -N DNSvalidate\n";
print "# and populate it with your zones\n";
while(<>){
        if(/^zone\s+"(.+)"\s+\{$/){
                $zone=$1;
                if($maxLengthOfQueryName){
                        $max=$maxLengthOfQueryName;
                } else {
                        open(DIG,"dig -t axfr +nocmd +nostats $zone |");
                        $max=0;
                        while(<DIG>){
                                if(/^(.+?)\.\s/){
                                        $max=(length($1)>$max)?length($1):$max;
                                }
                        }
                        close(DIG);
                }
                printf("iptables -A DNSvalidate -m string --from 40 --to %d --hex-string \"",($max+42));
                foreach $subdomain (split('\.',$zone)){
                        printf("|%02X|%s",length($subdomain),$subdomain);
                }
                print("|00|\" --algo bm -j RETURN -m comment --comment \"$zone\"\n");
        }
}
print "# and end the new chain with a drop\n";
print "/sbin/iptables -A DNSvalidate -j DROP\n";
print "# And, at last, make the new chain active (on UDP/53)\n";
print "/sbin/iptables -A INPUT -i $externalInterface -p udp --dport 53 -j DNSvalidate\n";

Execute o script acima com o arquivo de configuração da zona como argumento.

root:~/tmp/# ./zone2iptables.pl /etc/bind/named.conf.local 
# first time you run this, you will get error on the 3 first commands.
# It's here to make it safe/possible to periodically run this script.
/sbin/iptables -D INPUT -i eth1 -p udp --dport 53 -j DNSvalidate
/sbin/iptables -F DNSvalidate
/sbin/iptables -X DNSvalidate
#
# now, create the chain (again)
/sbin/iptables -N DNSvalidate
# and populate it with your zones
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|07|example|03|net|00|" --algo bm -j RETURN -m comment --comment "example.net"
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|07|example|03|com|00|" --algo bm -j RETURN -m comment --comment "example.com"
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|09|subdomain|02|of|07|example|02|nu|00|" --algo bm -j RETURN -m comment --comment "subdomain.of.example.nu"
# and end the new chain with a drop
/sbin/iptables -A DNSvalidate -j DROP
# And, at last, make the new chain active (on UDP/53)
/sbin/iptables -A INPUT -i eth1 -p udp --dport 53 -j DNSvalidate

Salve a saída em um script, coloque-o em um shell ou copie e cole no seu terminal para criar a nova cadeia e começar a filtrar todas as consultas DNS inválidas.

execute / sbin / iptables -L DNSvalidate -nvx para ver os contadores de pacotes (e bytes) em cada regra da nova cadeia (você pode mover a zona com a maioria dos pacotes para o topo da lista para torná-la mais eficiente).

Na esperança de que alguém possa achar isso útil :)

rico
fonte