enlace: agujero negro para consultas recursivas no válidas?

13

Tengo un servidor de nombres que es de acceso público ya que es el servidor de nombres autorizado para un par de dominios .

Actualmente, el servidor está inundado de ANYsolicitudes de tipo falsas para isc.org, ripe.net y similares (es un ataque DoS distribuido conocido ).

El servidor ejecuta BIND y se ha allow-recursionconfigurado en mi LAN para que estas solicitudes sean rechazadas. En tales casos, el servidor responde solo con authorityy additionalsecciones que hacen referencia a los servidores raíz.

¿Puedo configurar BIND para que ignore por completo estas solicitudes, sin enviar una respuesta?

Udo G
fuente

Respuestas:

5

Ante el mismo problema, elegí ignorar todas las solicitudes recursivas. Todos los solucionadores envían una consulta no recursiva cuando quieren usar mi servidor como un servidor autorizado. Solo los clientes y atacantes mal configurados, en mi propio caso, usan consultas recursivas.

Desafortunadamente, no he encontrado una manera de dejar que BIND haga eso, pero en caso de que iptables sea lo suficientemente bueno para ti, usé

iptables -t raw -I PREROUTING -i eth0 -p udp --destination-port 53 \
    -m string --algo kmp --from 30 \
    --hex-string "|01000001000000000000|" -j DROP
pino42
fuente
No, esa regla también bloquea las solicitudes de tipo autorizado (al menos en mi máquina). Aparentemente bloquea todo tipo de solicitudes DNS.
Udo G
Verifiqué dos veces y estoy usando exactamente esa regla. Aquí hay un cortar y pegar del servidor en vivo. Comando: iptables -t raw -S PREROUTING. Salida:, -P PREROUTING ACCEPTseguido de -A PREROUTING -i eth0 -p udp -m udp --dport 53 -m string --hex-string "|01000001000000000000|" --algo kmp --from 30 --to 65535 -j DROP. Probé que funciona correctamente con host -ar exampledomain.com dns-server.example.net. Por supuesto, no funcionó correctamente hasta que agregué la -ropción.
pino42
De acuerdo, la -ropción hace la diferencia. Personalmente, no me gusta que las hostconsultas simples ya no funcionen y esto puede ser muy confuso. Sin embargo, esta es probablemente una respuesta válida (la mejor hasta ahora) y le daré la recompensa, ya que está a punto de caducar, incluso si continuaré usando mi propio enfoque filtrando OUTPUT.
Udo G
¡Gracias! Si me encuentro con una solución mejor, me aseguraré de publicarla. Estoy de acuerdo contigo: este es un truco. Un trabajo, pero sigue siendo un truco.
pino42
2

Lo intentaré:

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

La zona de "redireccionamiento" controla las respuestas que remiten a los clientes a los servidores raíz. Esto debería decirle que no responda a esos.

Eso se insinúa en los documentos de Bind9: http://ftp.isc.org/isc/bind9/cur/9.9/doc/arm/Bv9ARM.ch06.html#id2592674

Puede reemplazar "none"con su subred local.

Si ya tiene una zone "."declaración, simplemente agréguela allow-query "none";.

freiheit
fuente
Tengo una zone "." { type hint; file "/etc/bind/db.root"; };declaración con db.root que enumera los servidores raíz. La eliminación de esa declaración detiene las respuestas para dominios extranjeros, pero el servidor responde con una "falla del servidor" y, por lo tanto, todavía se puede usar para DoS.
Udo G
@ UdoG: ¿Has intentado agregar allow-query "none";a la zone "."configuración?
Freiheit
Parece que esto solo está ahorrando ancho de banda ascendente, lo que debería ser abundante. Con su solución, el atacante ya ha agotado sus servidores de ancho de banda aguas abajo y potencia de procesamiento
TheLQ
@TheLQ: La pregunta se refiere a que se trata de un ataque DDoS. El ataque DDoS basado en DNS común es enviar consultas DNS con la IP forjada del objetivo. Como el paquete de respuesta DNS es más grande que la consulta, proporciona un multiplicador. Si su servidor no responde con un paquete significativamente más grande, ha eliminado la razón por la que está usando su servidor en el ataque.
Freiheit
@UdoG: el paquete de falla del servidor es solo de 31 a 32, mientras que la referencia a los servidores raíz probablemente fue de varios cientos de bytes. Si la respuesta de su servidor es del mismo tamaño que la consulta, o solo un poquito más grande, su servidor es inútil en un ataque DNS DDoS, ya que los atacantes consumirán tanto ancho de banda como lo envíen a su objetivo. Probé con una cantidad de servidores de nombres autoritarios probablemente bien configurados (como el de Google), y respondieron con "recursión solicitada pero no disponible".
freiheit
1

En general, sugeriría:

Active los registros de enlace y registre ips que reciban la respuesta rechazada. Instale el programa fail2ban, agregue la acción de agujero negro : http://pastebin.com/k4BxrAeG (ponga la regla en el archivo en /etc/fail2ban/actions.d)

Cree un archivo de filtro de enlace en /etc/fail2ban/filter.d con algo como esto (¡necesita depuración!)

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

Edite fail2ban.conf, agregue la sección:

[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 esto ayude!

Andrei Mikhaltsov
fuente
TODO: ejemplo de archivo de registro de enlace.
Andrei Mikhaltsov
1

La idea básica permite vincular clasifica la respuesta DNS como rechazada y luego usa iptables para convertir la respuesta rechazada en silenciosa ignorada.

Rechazar es la parte fácil en la sección de opciones named.conf:

allow-recursion { none;};

O, por supuesto, sus ACL favoritas para excepciones locales ...

Siguiente magia loca de iptables, ajusta o elimina "-o eth0" según sea necesario. Este comando asume el encabezado de capa IPv4 estándar de 20 bytes antes de UDP.

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

Estas teclas en el campo de banderas de respuesta DNS con los siguientes bits establecidos

  • Respuesta DNS
  • Consulta recursiva
  • Código de respuesta rechazado

Mensaje de registro notado que se ejecuta en enlace en la depuración "error al enviar respuesta: host inaccesible" cuando la regla coincide para tener algún comentario para la prueba.

Debo admitir que todo esto es un ejercicio algo inútil. Si no hay amplificación, un atacante podría reflejar fácilmente TCP SYN. En última instancia, el DNS está roto, simplemente no hay una solución viable que no sea el uso de TCP o la implementación de las cookies DNS de Eastlake.

comedor de discos
fuente
0

¿Has intentado bloquear la cadena isc.org o bloquear la cadena hexadecimal?

Esto funcionó para mí:

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

Maleficio
fuente
¿No sería mejor identificar las cadenas hexadecimales para todos los dominios a los que el servidor debería responder, hacer lo anterior para permitirlos y eliminar el resto del tráfico udp / 53?
Freiheit
Actualmente ya estoy bloqueando las respuestas UDP que se refieren a los servidores raíz: iptables -A OUTPUT -p udp -m string -hex-string "|726f6f742d73657276657273|" –algo bm –to 65535 -j DROPpero realmente preferiría una solución que se base solo en la configuración BIND, si es que es posible.
Udo G
Esto es débil. puedes generar cualquier aguijón que desees como dominio. estamos enfrentando ese problema en este momento y no es la forma de bloquearlo con un nombre estático'bnrexex.www.sf97.net/A/IN' 'whzpkacpxpiuycm.www.tpa.net.cn/A/IN'
3h4x
0

Este ataque se llama Denegación de servicio amplificada. Debe configurar el enlace correctamente, pero ese tráfico no debería llegar a su enlace en primer lugar. Bloquéelo en el primer dispositivo de red que sea capaz de hacerlo en su red. Tuve el mismo problema y lo solucioné con la regla de inhalación sorda:

alert udp $ EXTERNAL_NET any -> $ HOME_NET 53 (msg: "PROTOCOL-DNS consultas excesivas de tipo ANY - DoS potenciales"; byte_test: 1,! &, 0xF8,2; content: "| 00 00 FF 00 01 |"; detección_filtro: seguimiento por_src, recuento 30, segundos 30; metadatos: servicio dns; referencia: url, foxpa.ws / 2010/07/21 / thwarting-the-isc-org-dns-ddos /; classtype: intento-dos; sid : 21817; rev: 4;)

3h4x
fuente
0

Primero, sé que esta es una vieja pregunta pero ...

He estado ejecutando mi propio servidor DNS autorizado, no recursivo, durante décadas, pero nunca he sido víctima de ningún ataque DDoS basado en DNS, hasta ahora, cuando me cambié a un nuevo ISP. Miles de consultas falsas de DNS inundaron mis registros y me molesté mucho, no tanto por el impacto en mi servidor, sino por el hecho de que saturaba mis registros y la incómoda sensación de abuso. Parece que el atacante intenta usar mi DNS en un " ataque autorizado del servidor de nombres ".

Entonces pensé que, aunque limito las consultas recursivas a mi red interna (negando todas las demás), prefiero pasar mis ciclos de CPU en la coincidencia de cadenas en iptables que enviar respuestas negativas a las direcciones IP falsificadas (menos desorden en mis registros, menos tráfico de red y un mayor nivel de satisfacción propio).

Comencé haciendo lo que todos los demás parecen hacer , averiguar qué nombres de dominio se consultan y crear una coincidencia de cadena en ese dominio con un DROP de destino. Pero pronto me di cuenta de que terminaría con una gran cantidad de reglas, cada una de las cuales consumía ciclos de CPU. ¿Entonces lo que hay que hacer? Como no ejecuto un servidor de nombres recursivo, pensé que podría hacer la coincidencia en las zonas reales para las que tengo autoridad y descartar todo lo demás.

Mi política predeterminada en iptables es ACEPTAR, si su política es DROP, probablemente necesite hacer algunos ajustes si desea utilizar la siguiente solución.

Mantengo mi configuración de zona en un archivo separado (/etc/bind/named.conf.local), usemos esto como ejemplo:

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;
};

Tenga en cuenta el comentario "// Privado" en mis dos primeras zonas, hago uso de esto en el siguiente script para excluirlos de la 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";

Ejecute el script anterior con el archivo de configuración de 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

Guarde el resultado en un script, canalícelo a un shell o cópielo y péguelo en su terminal para crear la nueva cadena y comenzar a filtrar todas las consultas DNS no válidas.

ejecute / sbin / iptables -L DNSvalidate -nvx para ver los contadores de paquetes (y bytes) en cada regla de la nueva cadena (es posible que desee mover la zona con la mayoría de los paquetes al principio de la lista para que sea más eficiente).

Con la esperanza de que alguien pueda encontrar esto útil :)

Rico
fuente