bind: blackhole per query ricorsive non valide?


13

Ho un server dei nomi accessibile pubblicamente poiché è il server dei nomi autorevole per un paio di domini .

Attualmente il server è invaso da ANYrichieste di tipo falso per isc.org, ripe.net e simili (è un attacco DoS distribuito noto ).

Il server esegue BIND e si è allow-recursionimpostato sulla mia LAN in modo che queste richieste vengano rifiutate. In tali casi il server risponde solo con authoritye additionalsezioni che fanno riferimento ai server root.

Posso configurare BIND in modo che ignori completamente queste richieste, senza inviare alcuna risposta?

Risposte:


5

Di fronte allo stesso problema, ho scelto di ignorare tutte le richieste ricorsive. Tutti i resolver inviano una query non ricorsiva quando desiderano utilizzare il mio server come server autorevole. Solo i client e gli aggressori non configurati correttamente, nel mio caso, usano query ricorsive.

Sfortunatamente non ho trovato un modo per lasciare che BIND lo faccia, ma nel caso in cui iptables sia abbastanza buono per te, l'ho usato

iptables -t raw -I PREROUTING -i eth0 -p udp --destination-port 53 \
    -m string --algo kmp --from 30 \
    --hex-string "|01000001000000000000|" -j DROP

No, quella regola blocca anche le richieste di tipo autorevole (almeno sulla mia macchina). Apparentemente blocca tutti i tipi di richieste DNS.
Udo G,

Ho ricontrollato e sto usando esattamente quella regola. Ecco una copia e incolla dal server live. Comando: iptables -t raw -S PREROUTING. Output:, -P PREROUTING ACCEPTseguito da -A PREROUTING -i eth0 -p udp -m udp --dport 53 -m string --hex-string "|01000001000000000000|" --algo kmp --from 30 --to 65535 -j DROP. Ho provato che funziona correttamente con host -ar exampledomain.com dns-server.example.net. Ovviamente non ha funzionato correttamente fino a quando non ho aggiunto l' -ropzione.
pino42,

Va bene, l' -ropzione fa la differenza. Personalmente non mi piace che le semplici hostquery non funzionino più e questo può essere molto confuso. Questa è probabilmente una risposta valida (la migliore finora) e comunque ti darò la generosità, poiché sta per scadere, anche se continuerò a usare il mio approccio filtrando OUTPUT.
Udo G

Grazie! Se trovo una soluzione migliore, mi assicurerò di pubblicarla. Sono d'accordo con te: questo è un trucco. Funzionante, ma comunque un trucco.
pino42,

2

Proverei:

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

Le risposte che indirizzano i client ai server root sono controllate dalla zona di "reindirizzamento". Questo dovrebbe dirgli di non rispondere a quelli.

Questo è accennata nei documenti Bind9: http://ftp.isc.org/isc/bind9/cur/9.9/doc/arm/Bv9ARM.ch06.html#id2592674

È possibile sostituire "none"con la sottorete locale.

Se hai già un zone "." dichiarazione, aggiungi semplicemente allow-query "none";ad essa.


Ho una zone "." { type hint; file "/etc/bind/db.root"; };dichiarazione con db.root che elenca i server root. La rimozione di tale dichiarazione interrompe la risposta a domini esterni ma il server risponde comunque con un "errore del server" e può quindi essere ancora utilizzato per DoS.
Udo G,

@UdoG: hai provato ad aggiungere allow-query "none";alla zone "."configurazione?
Freiheit,

Sembra che questo stia solo risparmiando larghezza di banda a monte, anche se dovrebbe essere abbondante. Con la tua correzione, l'attaccante ha già esaurito la larghezza di banda a valle e la potenza di elaborazione dei tuoi server
TheLQ

@TheLQ: la domanda si riferisce al fatto che si tratta di un attacco DDoS. Il comune attacco DDoS basato su DNS è l'invio di query DNS con l'IP forgiato della destinazione. Poiché il pacchetto di risposta DNS è più grande della query, fornisce un moltiplicatore. Se il tuo server non risponde con un pacchetto significativamente più grande, hai eliminato il motivo per cui stanno usando il tuo server durante l'attacco.
Freiheit,

@UdoG: il pacchetto di errori del server è solo da 31 a 32, mentre il riferimento ai server root era probabilmente di diverse centinaia di byte. Se la risposta del tuo server ha le stesse dimensioni della query, o solo un po 'più grande, il tuo server è inutile in un attacco DDoS DNS, poiché gli aggressori consumeranno tutta la larghezza di banda che ti invieranno al loro obiettivo. Ho testato un numero di server dei nomi autorevoli probabilmente ben configurati (come quelli di Google) e hanno risposto con "ricorsione richiesta ma non disponibile".
freiheit,

1

In generale, suggerirei:

Attiva i registri di bind e registra ips che ottengono la risposta rifiutata. Installa il programma fail2ban, aggiungi l'azione blackhole: http://pastebin.com/k4BxrAeG (inserisci la regola nel file in /etc/fail2ban/actions.d)

Crea il file del filtro bind in /etc/fail2ban/filter.d con qualcosa del genere (è necessario il debug!)

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

Modifica fail2ban.conf, aggiungi sezione:

[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

Spero che questo possa aiutare!


TODO: esempio di file di registro associato.
Andrei Mikhaltsov,

1

L'idea di base consente a bind di classificare la risposta DNS come Rifiutato, quindi utilizzare iptables per convertire Rifiutato in Silenziosamente ignorato.

Rifiutato è la parte facile nella sezione delle opzioni di named.conf:

allow-recursion { none;};

O ovviamente i tuoi ACL preferiti per le eccezioni locali ...

Avanti pazzo iptables magia, regolare o rimuovere "-o eth0" secondo necessità. Questo comando presuppone un'intestazione di livello IPv4 standard da 20 byte prima di UDP.

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

Questa chiave sul campo flag della risposta DNS con i seguenti bit impostati

  • Risposta DNS
  • Query ricorsiva
  • Codice di risposta rifiutato

Il messaggio di registro notato in esecuzione si associa al debug "errore durante l'invio della risposta: host non raggiungibile" quando la regola corrisponde a un feedback per il test.

Devo ammettere che questo è tutto un esercizio un po 'inutile. Se non c'è amplificazione, un utente malintenzionato potrebbe altrettanto facilmente riflettere TCP SYN. Alla fine il DNS non ha funzionato semplicemente con una soluzione valida se non l'utilizzo del TCP o la distribuzione dei cookie DNS di Eastlake.


0

Hai provato a bloccare la stringa isc.org o bloccare la stringa esadecimale per essa?

Questo ha funzionato per me:

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


Non sarebbe meglio identificare le stringhe esadecimali per tutti i domini a cui il server dovrebbe rispondere, fare quanto sopra per consentirle e eliminare tutti gli altri traffici udp / 53?
freiheit,

Attualmente sto già bloccando le risposte UDP che si riferiscono ai server root: iptables -A OUTPUT -p udp -m string -hex-string "|726f6f742d73657276657273|" –algo bm –to 65535 -j DROPma preferirei davvero una soluzione che si basa solo sulla configurazione BIND, se possibile.
Udo G,

questo è debole. puoi generare qualsiasi puntura che desideri come dominio. stiamo affrontando quel problema in questo momento e non è il modo di bloccarlo tramite un nome statico'bnrexex.www.sf97.net/A/IN' 'whzpkacpxpiuycm.www.tpa.net.cn/A/IN'
3h4x

0

Questo attacco è chiamato Denial of Service amplificato. Dovresti configurare correttamente il bind ma quel traffico non dovrebbe arrivare al tuo bind in primo luogo. Bloccalo sul primo dispositivo di rete in grado di farlo nella tua rete. Ho avuto lo stesso problema e l'ho risolto con una regola di sbuffo sordo:

alert udp $ EXTERNAL_NET any -> $ HOME_NET 53 (msg: "PROTOCOL-DNS query eccessive di tipo ANY - potenziale DoS"; byte_test: 1,! &, 0xF8,2; content: "| 00 00 FF 00 01 |"; detect_filter: traccia by_src, conteggio 30, secondi 30; metadati: servizio dns; riferimento: url, foxpa.ws / 2010/07/21 / thwarting-isc-org-dns-ddos /; classtype: tentato-dos; sid : 21817; giro: 4;)


0

Innanzitutto, so che questa è una vecchia domanda ma ...

Ho gestito il mio server DNS autorevole, non ricorsivo per decenni, ma non sono mai stato vittima di alcun attacco DDoS basato su DNS - fino ad ora, quando sono passato a un nuovo ISP. Migliaia di query DNS contraffatte hanno inondato i miei registri e mi sono davvero infastidito, non tanto per l'impatto sul mio server, piuttosto per il fatto che ingombra i miei registri e la scomoda sensazione di essere abusato. Sembra che l'attaccante cerchi di usare il mio DNS in un " attacco autorevole di Name Server ".

Quindi ho pensato che, anche se ho limitato le query ricorsive alla mia rete interna (negandone tutte le altre), preferisco spendere i miei cicli della CPU sulla corrispondenza delle stringhe in iptables piuttosto che inviare risposte negative agli indirizzi IP falsificati (meno disordine nei miei registri, meno traffico di rete e un livello di soddisfazione più alto del mio).

Ho iniziato facendo come tutti gli altri sembrano fare , scopri quali nomi di dominio vengono interrogati e ho creato una corrispondenza di stringa su quel dominio con un DROP di destinazione. Ma presto mi sono reso conto che avrei finito con un'enorme quantità di regole, ognuna delle quali consumava cicli di CPU. Quindi che si fa? Dal momento che non eseguo un server dei nomi ricorsivo, ho pensato che avrei potuto fare la corrispondenza sulle zone effettive per cui sono autorevole e rilasciare tutto il resto.

La mia politica di default in iptables è ACCETTA, se la tua politica è DROP probabilmente dovrai apportare alcune modifiche se desideri utilizzare la seguente soluzione.

Mantengo la configurazione della mia zona in un file separato (/etc/bind/named.conf.local), usiamo questo come esempio:

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

Nota il commento "// privato" sulle mie prime due zone, ne faccio uso nel seguente script per escluderle dall'elenco delle zone valide.

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

Eseguire lo script sopra con il file di configurazione della zona come argomento.

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

Salvare l'output in uno script, inserirlo in una shell o copiarlo e incollarlo nel terminale per creare la nuova catena e iniziare a filtrare tutte le query DNS non valide.

esegui / sbin / iptables -L DNSvalidate -nvx per vedere i contatori di pacchetti (e byte) su ogni regola nella nuova catena (potresti voler spostare la zona con la maggior parte dei pacchetti in cima all'elenco per renderla più efficiente).

Nella speranza che qualcuno possa trovare questo utile :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.