bind: trou noir pour les requêtes récursives invalides?

13

J'ai un serveur de noms accessible au public car c'est le serveur de noms faisant autorité pour quelques domaines .

Actuellement, le serveur est inondé de ANYdemandes de type falsifiées pour isc.org, ripe.net et similaires (c'est une attaque DoS distribuée connue ).

Le serveur exécute BIND et a allow-recursiondéfini mon LAN pour que ces demandes soient rejetées. Dans de tels cas, le serveur répond uniquement avec authorityet des additionalsections référençant les serveurs racine.

Puis-je configurer BIND pour qu'il ignore complètement ces demandes, sans envoyer de réponse du tout?

Udo G
la source

Réponses:

5

Face au même problème, j'ai choisi d'ignorer toutes les requêtes récursives. Tous les résolveurs envoient une requête non récursive lorsqu'ils souhaitent utiliser mon serveur comme serveur faisant autorité. Seuls les clients et les attaquants mal configurés, dans mon cas, utilisent des requêtes récursives.

Malheureusement, je n'ai pas trouvé de moyen de laisser BIND faire cela, mais si iptables vous convient, j'ai utilisé

iptables -t raw -I PREROUTING -i eth0 -p udp --destination-port 53 \
    -m string --algo kmp --from 30 \
    --hex-string "|01000001000000000000|" -j DROP
pino42
la source
Non, cette règle bloque également les demandes de type faisant autorité (au moins sur ma machine). Apparemment, il bloque toutes sortes de requêtes DNS.
Udo G
J'ai revérifié et j'utilise exactement cette règle. Voici un copier-coller du serveur en direct. Commande: iptables -t raw -S PREROUTING. Sortie:, -P PREROUTING ACCEPTsuivie de -A PREROUTING -i eth0 -p udp -m udp --dport 53 -m string --hex-string "|01000001000000000000|" --algo kmp --from 30 --to 65535 -j DROP. J'ai testé qu'il fonctionne correctement avec host -ar exampledomain.com dns-server.example.net. Bien sûr, cela n'a pas fonctionné correctement jusqu'à ce que j'ajoute l' -roption.
pino42
D'accord, l' -roption fait la différence. Personnellement, je n'aime pas que les hostrequêtes simples ne fonctionnent plus et cela peut être très déroutant. C'est probablement une réponse valable (la meilleure jusqu'à présent) et je vais vous donner la prime, car elle est sur le point d'expirer, même si je continuerai à utiliser ma propre approche en filtrant OUTPUT.
Udo G
Merci! Si je tombe sur une meilleure solution, je m'assurerai de la publier. Je suis d'accord avec toi: celui-ci est un hack. Un travail, mais toujours un hack.
pino42
2

Je voudrais essayer:

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

Les réponses référant les clients aux serveurs racine sont contrôlées par la zone "rediriger". Cela devrait lui dire de ne pas y répondre.

Cela est indiqué dans les documents Bind9: http://ftp.isc.org/isc/bind9/cur/9.9/doc/arm/Bv9ARM.ch06.html#id2592674

Vous pouvez remplacer "none"par votre sous-réseau local.

Si vous avez déjà une zone "."déclaration, ajoutez-la simplement allow-query "none";.

freiheit
la source
J'ai une zone "." { type hint; file "/etc/bind/db.root"; };déclaration avec db.root listant les serveurs racine. La suppression de cette déclaration arrête les réponses pour les domaines étrangers, mais le serveur répond néanmoins par une "défaillance du serveur" et peut donc toujours être utilisé pour DoS.
Udo G
@UdoG: Avez-vous essayé d'ajouter allow-query "none";à la zone "."config?
freiheit
Il semble que cela n'économise que la bande passante en amont, ce qui devrait être abondant. Avec votre correctif, l'attaquant a déjà utilisé la bande passante et la puissance de traitement de vos serveurs en aval
TheLQ
@TheLQ: La question fait référence à une attaque DDoS. L'attaque DDoS commune basée sur DNS consiste à envoyer des requêtes DNS avec l'IP falsifiée de la cible. Étant donné que le paquet de réponse DNS est plus grand que la requête, il fournit un multiplicateur. Si votre serveur ne répond pas avec un paquet beaucoup plus important, vous avez éliminé la raison pour laquelle ils utilisent votre serveur dans l'attaque.
freiheit
@UdoG: Le paquet de panne de serveur n'est que de 31 à 32, tandis que la référence aux serveurs racine était probablement de plusieurs centaines d'octets. Si la réponse de votre serveur est de la même taille que la requête, ou seulement un tout petit peu plus grande, votre serveur est inutile dans une attaque DNS DDoS, car les attaquants consommeront autant de bande passante qu'ils vous feront envoyer à leur cible. J'ai testé contre un certain nombre de serveurs de noms bien configurés (tels que Google) et ils ont répondu "récursivité demandée mais non disponible".
freiheit
1

En général, je suggère:

Activez les journaux de liaison et enregistrez les ips dont la réponse est rejetée. Installez le programme fail2ban, ajoutez une action blackhole: http://pastebin.com/k4BxrAeG (mettez la règle dans un fichier dans /etc/fail2ban/actions.d)

Créez un fichier de filtre de liaison dans /etc/fail2ban/filter.d avec quelque chose comme ça (nécessite un débogage!)

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

Modifiez fail2ban.conf, ajoutez une section:

[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

J'espère que cela vous aidera!

Andrei Mikhaltsov
la source
TODO: exemple de fichier journal de liaison.
Andrei Mikhaltsov
1

L'idée de base permet à bind de classer la réponse DNS comme Refus, puis d'utiliser iptables pour convertir Refused en silencieusement ignoré.

Refusé est la partie facile dans la section d'options named.conf:

allow-recursion { none;};

Ou bien sûr vos listes de contrôle d'accès préférées pour les exceptions locales ...

Suivant la magie folle d'iptables, ajustez ou supprimez "-o eth0" au besoin. Cette commande suppose un en-tête de couche IPv4 standard de 20 octets avant UDP.

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

Cette clé sur le champ des drapeaux de la réponse DNS avec les bits suivants définis

  • Réponse DNS
  • Requête récursive
  • Code de réponse refusé

Message de journal remarqué exécutant la liaison dans le débogage "erreur d'envoi de réponse: hôte inaccessible" lorsque la règle correspond pour avoir des commentaires à tester.

Je dois admettre que tout cela est un exercice quelque peu inutile. S'il n'y a pas d'amplification, un attaquant pourrait tout aussi bien refléter TCP SYN. En fin de compte, le DNS n'est simplement pas une solution viable autre que l'utilisation de TCP ou le déploiement des cookies DNS d'Eastlake.

mangeur de disque
la source
0

Avez-vous essayé de bloquer la chaîne isc.org ou de bloquer la chaîne hexadécimale pour cela?

Cela a fonctionné pour moi:

iptables -A ENTRÉE -p udp -m chaîne --hex-chaîne "| 03697363036f726700 |" --algo bm -j DROP

Hex
la source
Ne serait-il pas préférable d'identifier les chaînes hexadécimales pour tous les domaines auxquels le serveur devrait répondre, de faire ce qui précède pour les autoriser et de supprimer tous les autres trafics udp / 53?
freiheit
Je bloque actuellement déjà les réponses UDP qui se réfèrent aux serveurs racine: iptables -A OUTPUT -p udp -m string -hex-string "|726f6f742d73657276657273|" –algo bm –to 65535 -j DROPmais je préférerais vraiment une solution basée uniquement sur la configuration BIND, si cela est possible.
Udo G
c'est faible. vous pouvez générer la piqûre que vous souhaitez en tant que domaine. nous sommes confrontés à ce problème en ce moment et ce n'est pas le moyen de le bloquer via un nom statique'bnrexex.www.sf97.net/A/IN' 'whzpkacpxpiuycm.www.tpa.net.cn/A/IN'
3h4x
0

Cette attaque est appelée déni de service amplifié. Vous devez configurer correctement la liaison, mais ce trafic ne doit pas atteindre votre liaison en premier lieu. Bloquez-le sur le premier périphérique réseau capable de le faire sur votre réseau. J'ai eu le même problème et je l'ai traité avec la règle de snort sourd:

alert udp $ EXTERNAL_NET any -> $ HOME_NET 53 (msg: "PROTOCOL-DNS requêtes excessives de type ANY - potential DoS"; byte_test: 1,! &, 0xF8,2; content: "| 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-the-isc-org-dns-ddos /; classtype: tentative de dos; sid : 21817; rev: 4;)

3h4x
la source
0

Tout d'abord, je sais que c'est une vieille question mais ...

Je gère mon propre serveur DNS non récursif faisant autorité depuis des décennies, mais je n'ai jamais été victime d'attaques DDoS basées sur DNS - jusqu'à présent, lorsque je suis passé à un nouveau FAI. Des milliers de requêtes DNS usurpées ont inondé mes journaux et je me suis vraiment ennuyé - pas tant sur l'impact sur mon serveur, plutôt sur le fait qu'il encombrait mes journaux et le sentiment inconfortable d'être abusé. Il semble que l'attaquant essaie d'utiliser mon DNS dans une « attaque de serveur de noms faisant autorité ».

J'ai donc pensé que, même si je limitais les requêtes récursives à mon réseau interne (en refusant tous les autres), je préférais passer mes cycles de processeur à faire correspondre les chaînes dans iptables plutôt qu'à renvoyer des réponses négatives aux adresses IP usurpées (moins d'encombrement dans mes journaux, moins trafic réseau et un niveau de satisfaction plus élevé).

J'ai commencé par faire comme tout le monde semble le faire , savoir quels noms de domaine sont interrogés et créé une correspondance de chaîne sur ce domaine avec un DROP cible. Mais je me suis vite rendu compte que je me retrouverais avec une énorme quantité de règles, chacune consommant des cycles CPU. Alors que faire? Étant donné que je n'exécute pas de serveur de noms récursif, j'ai pensé que je pourrais faire la correspondance sur les zones réelles pour lesquelles je fais autorité et supprimer tout le reste.

Ma politique par défaut dans iptables est ACCEPT, si votre politique est DROP, vous devrez probablement faire quelques ajustements si vous souhaitez utiliser la solution suivante.

Je garde ma configuration de zone dans un fichier séparé (/etc/bind/named.conf.local), utilisons ceci comme exemple:

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

Notez le commentaire «// Private» sur mes deux premières zones, je m'en sers dans le script suivant pour les exclure de la liste des zones valides.

#!/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";

Exécutez le script ci-dessus avec le fichier de configuration de zone comme argument.

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

Enregistrez la sortie dans un script, dirigez-la vers un shell ou copiez-collez-la dans votre terminal pour créer la nouvelle chaîne et commencer à filtrer toutes les requêtes DNS non valides.

exécutez / sbin / iptables -L DNSvalidate -nvx pour voir les compteurs de paquets (et d'octets) sur chaque règle de la nouvelle chaîne (vous voudrez peut-être déplacer la zone avec la plupart des paquets en haut de la liste pour la rendre plus efficace).

En espérant que quelqu'un puisse trouver cela utile :)

riches
la source