NFsec Logo

DNS – raport miliona zapytań

12/03/2016 w Bezpieczeństwo 2 komentarze.  (artykuł nr 510, ilość słów: 1583)

P

o napisaniu podstaw bezpieczeństwa DNS zastanawiałem się, jaka jest skala problemu źle skonfigurowanych serwerów, które pozwalają na ściągnięcie całej strefy lub wykonywanie zapytań dowolnemu klientowi. Duch patryjotyzmu namawiał mnie na sprawdzenie TLD .pl, zarządzaną przez NASK. Niestety, jak zawsze polski dwór zakończył nadzieję na czwartej wymianie zdania. Dialog wyglądał tak:

– Czy istnieje możliwość otrzymania od Państwa listy wszystkich dotychczas zarejestrowanych domen .pl?
– NASK nie udostępnia tego typu informacji.
– A orientują się Państwo do jakiego organu można zwrócić się w tej sprawie?
– Te dane posiada tylko NASK.

Nie chciało mi się już próbować, czy aby jednak istnieje taka możliwość poprzez CZDS. Rozważałem już przerzucić się na zagraniczne banany, ale z pomocą przyszedł portal, który stanowi swoiste źródło wiedzy o ruchu w Internecie. Okazuje się, że Alexa bez żadnych zbędnych formalności udostępnia milion najczęściej odwiedzanych stron. To stanowi już jakąś próbkę, którą można poddać testom.

Lista domen jest w formacie CSV, więc idealnie nadaje się do obróbki za pomocą dowolnego skryptu. Po usunięciu liczb porządkowych (cat top-1m.csv | sed 's/[0-9]*,//' > top-1m.txt) przyszedł czas na pierwszy test transferu domen (AXFR). W tym celu wykorzystałem już istniejący skrypt. Aby uzyskać przyśpieszenie testu podzieliłem plik na dwie równe części (split -l 500000 top-1m.txt) i zmusiłem RPi2 do pracy w dzień i w nocy:

python3 axfr-test.py -i xaa -o zones.txt -l alexia.log -p 3
python3 axfr-test.py -i xab -o zones2.txt -l alexia2.log -p 3

Zanim przejdziemy do wyników tego testu spójrzmy z jaką ilością poszczególnych domen TLD (top 100) się mierzyliśmy. Usuńmy wszystkie znaki do ostatniej kropki (sed 's/^.*\././') i policzmy te same wstąpienia:

#!/usr/bin/env python
import collections
data = [line.rstrip('\n') for line in open("top-1m.txt")]
stats = collections.Counter(data)
for k, v in stats.iteritems():
    print str(k)+","+str(v)

Dane zapisujemy ponownie w formacie CSV i importujemy je do arkuszu, sortujemy i konwertujemy do tabeli HTML. Oto wyniki:

 Domena TLD:  Ilość wystąpień: 
.com 504490
.net 51643
.ru 49564
.org 43697
.jp 23532
.de 23136
.in 17400
.uk 16611
.br 16313
.it 12062
.ir 11622
.pl 11581
.cn 10761
.fr 10481
.info 10280
.au 7758
.nl 7514
.es 6816
.gr 6051
.co 5515
.ua 5160
.kr 5021
.cz 4798
.ca 4592
.tw 4591
.tr 4405
.vn 4382
.eu 4196
.za 4051
.ro 3907
.tv 3852
.mx 3764
.id 3640
.me 3504
.edu 3338
.biz 3327
.se 3178
.hu 2916
.ch 2526
.us 2456
.ar 2418
.be 2346
.at 2241
.dk 2103
.xyz 2074
.sk 1978
.no 1937
.io 1873
.fi 1678
.cc 1657
.by 1548
.pt 1522
.my 1517
.il 1456
.pk 1441
.hk 1417
.sg 1343
.kz 1333
.cl 1204
.az 1186
.nz 1142
.ie 1139
.lt 1102
.gov 1098
.su 1016
.th 969
.xn--p1ai 918
.bg 900
.ae 893
.pe 854
.rs 826
.hr 772
.ng 735
.eg 727
.lv 701
.ph 675
.asia 658
.mobi 657
.sa 645
.tk 639
.club 617
.ws 599
.si 590
.ee 553
.pro 521
.ma 515
.to 503
.uz 495
.lk 484
.pw 482
.ve 402
.ge 398
.fm 395
.xxx 393
.bd 380
.is 328
.cat 327
.am 322
.nu 316
.tn 300

Pełną listę domen TLD można pobrać tutaj.

Z logów skryptu axfr-test.py do transferu plików stref wyciągnijmy ile zostało wykrytych cieknących domen oraz ile obsługujących je serwerów DNS:

root@erpi2:~# cat alexia.log alexia2.log > cumulative.log
root@erpi2:~# grep -c "Success" cumulative.log > clean.txt
root@erpi2:~# cat clean.txt | sed 's/.*@ //' | uniq | wc -l
105572
root@erpi2:~# grep "Success" cumulative.log | sed 's/ @.*//' | uniq | wc -l
61046

Liczba serwerów DNS, która pozwoliła mi na pełny transfer strefy wynosi 105.572 tysięcy (10.5%) – sprowadzając to do poziomu domen liczba ta spada do wartości 61.046 tysięcy (6.1%). Nie muszę chyba przypominać, że uzyskanie dostępu do całej strefy DNS danej domeny to tak, jak zadzwonienie do recepcji danej firmy i poproszenie sekretarki, aby podała numery telefonów do wszystkich działów – wymieniając przy tym jeszcze nazwy tych działów bo ich też nie znamy. Miła pani nie potwierdzając naszych uprawnień do tych informacji dodatkowo zdradza znam przy okazji, który dział jest nad jakim i od jakiego zależy. Nie trzeba skanować, zgadywać, knuć. Tylko planować uderzenie. Bez problemu w strefach mogłem znaleźć informacje o standardowych usługach i ich adresach:

– usługi wymiany plików (ftp, webupload, webdisk),
– wersje demonstracyjne webaplikacji dla potencjalnych klientów (demo-*),
– wersje developerskie usług (dev-*, test-*),
– systemy do monitoringu serwerów (munin, nagios, sentry),
– panele administracyjne (admin*, webmail, manager, cpanel, whm),
– systemy do dokumentacji (wiki, sphinx, rtd),
– systemy do code review oraz rozproszone systemy plików (git, svn, gerrit, fisheye),
– systemy do analizy logów (elasticsearch-head, kibana),
– rodzaje stosowanych komunikatorów (lync, hipchat, msn, xmpp, skype),
– i wiele innych mówiących same za siebie (jira, outlook, vpn, zimbra).

Ogólnie z transferu wszystkich podatnych domen zebrało się 186 MB danych gotowych do dalszej analizy. Jednak to nie wszystko. Jeśli spojrzymy na ten przypadek z poziomu serwera DNS to możemy uzyskać znacznie więcej informacji. Skoro odkryliśmy adres serwera umożliwiającego bezkarny transfer strefy jednej domeny to istnieje bardzo duże prawdopodobieństwo, że jego nieprawidłowa konfiguracja pozwala na to samo dla dowolnej innej domeny, którą obsługuje. Jak sprawdzić jakie inne domeny obsługuje dany serwer DNS? Wiele serwisów oferuje przeszukiwanie różnych swoich baz.

Przejdźmy do drugiej części testu, czyli wyszukaniu serwerów typu open resolver i sprawdźmy ile serwerów byśmy mogli wykorzystać np. do ataku dns amplification. W tym celu na podstawie AXFR-Test na szybko zmontowałem skrypt recurser.py:

#!/usr/bin/python3

import sys
import dns.resolver


def check_ns(domain):
    nslist = []
    domain = domain.strip()
    try:
        nsq = dns.resolver.query(domain, "NS")
        for ns in nsq.rrset:
            nserver = str(ns)[:-1]
            if nserver is None or nserver == "":
                continue
            else:
                nslist.append(nserver)
    except Exception as e:
        pass
    if not nslist:
        pass
    else:
        print("---")
        print("Checking: " + domain + " with NS servers:", nslist)
        resolve_ns(nslist)


def resolve_ns(nslist):
    iplist = []
    rns = dns.resolver.Resolver()
    for ns in nslist:
        try:
            nsip = rns.query(ns, "A")
            for rdata in nsip:
                print("NS: " + ns + " have IP: " + rdata.address)
                iplist.append(rdata.address)
        except Exception as e:
            pass
    if not iplist:
        pass
    else:
        check_recurse(iplist)


def check_recurse(iplist):
    for ip in iplist:
        try:
            rns = dns.resolver.Resolver(configure=False)
            rns.timeout = 2
            rns.lifetime = 5
            rns.nameservers = [ip]
            check = rns.query("nfsec.pl", "A")
            for rdata in check:
                print("Knocking to: " + ip + " = OpenResolver?")
                print(rdata)
        except dns.resolver.NoNameservers:
            print("Knocking to: " + ip + " = ClosedResolver!")
        except dns.resolver.NoAnswer:
            print("Knocking to: " + ip + " = SilenceOnTheWire!")
        except Exception as e:
            pass
    print("---")


if __name__ == '__main__':
    if len(sys.argv) < = 1:
        print("Usage: " + sys.argv[0] + " domain.com")
    else:
        check_ns(sys.argv[1])

Obliczmy ile serwerów rozwiązało domenę nfsec.pl i zwróciło jej prawidłowy adres IP (wiele serwerów DNS odpowiada na zewnętrzne zapytania o inne domeny, ale zwraca adres IP serwera www typu catch-all w ten sposób przekierowując użytkownika na strony z podstawionymi treściami):

for i in `cat top-1m.txt`; do python3 recurser.py $i 2>&1 >> openresolvers.log; done
grep -B 1 '37.187.104.217' openresolvers.log | grep -v "\--" > clean.txt
grep 'OpenResolver?' clean.txt | uniq | wc -l
49519

Razem wyszło 49.519 tysięcy (4.95%) otwartych serwerów, co daje już mały botnet do przeprowadzania ataków. Skoro zebraliśmy już te wszystkie dane wygenerujmy listę top 100 najczęściej używanych serwerów DNS z wszystkich domen, które odpowiedziały na zapytania i zwróciły nam ich listę:

grep '\[' openresolvers.log | sed 's/.*\[/[/' > topdns.txt
#!/usr/bin/env python
import collections
import ast

topdns = []
for line in open("topdns.txt"):
    line = ast.literal_eval(line)
    topdns.extend(line)

stats = collections.Counter(topdns)
for k, v in stats.iteritems():
    print str(k)+","+str(v)

Analogicznie – dane za pomocą przekierowania strumienia zapisujemy ponownie w formacie CSV, importujemy je do arkuszu, sortujemy i konwertujemy do tabeli HTML. Oto wyniki:

 Serwer DNS:  Ilość wystąpień: 
f1g1ns1.dnspod.net 10518
f1g1ns2.dnspod.net 10516
ns2.bluehost.com 9374
ns1.bluehost.com 9373
dns4.registrar-servers.com 7207
dns5.registrar-servers.com 7206
dns3.registrar-servers.com 7206
dns2.registrar-servers.com 7206
dns1.registrar-servers.com 7204
ns1.dreamhost.com 6667
ns2.dreamhost.com 6667
ns3.dreamhost.com 6663
dns3.name-services.com 5589
dns2.name-services.com 5589
dns1.name-services.com 5589
dns4.name-services.com 5588
dns5.name-services.com 5573
ns2.dns.ne.jp 5339
ns1.dns.ne.jp 5331
ns1.dnsmadeeasy.com 4947
ns3.dnsmadeeasy.com 4932
ns0.dnsmadeeasy.com 4915
ns2.dnsmadeeasy.com 4889
ns4.dnsmadeeasy.com 4867
dns2.stabletransit.com 4680
dns1.stabletransit.com 4677
ns1.digitalocean.com 4660
ns2.digitalocean.com 4657
ns3.digitalocean.com 4632
a.dns.gandi.net 4584
b.dns.gandi.net 4583
c.dns.gandi.net 4583
02.dnsv.jp 4403
01.dnsv.jp 4403
ns1.mediatemple.net 4401
ns2.mediatemple.net 4399
ns.rackspace.com 4272
ns2.rackspace.com 4267
ns11.dnsmadeeasy.com 4198
ns12.dnsmadeeasy.com 4196
ns10.dnsmadeeasy.com 4189
ns13.dnsmadeeasy.com 4178
ns14.dnsmadeeasy.com 4139
ns15.dnsmadeeasy.com 4097
03.dnsv.jp 3960
04.dnsv.jp 3960
ns1.xserver.jp 3856
ns3.xserver.jp 3856
ns2.xserver.jp 3856
dns.technorail.com 3851
dns2.technorail.com 3851
dns4.arubadns.cz 3830
dns3.arubadns.net 3830
ns5.xserver.jp 3792
ns4.xserver.jp 3791
ns1.linode.com 3449
ns2.linode.com 3446
ns3.linode.com 3446
ns4.linode.com 3441
ns5.linode.com 3424
dns2.yandex.net 3257
dns1.yandex.net 3257
ns4-l2.nic.ru 3128
ns2.beget.ru 3087
ns1.beget.ru 3087
ns2.beget.pro 3016
ns1.beget.pro 3016
ns8-l2.nic.ru 3015
ns2.reg.ru 3011
ns1.reg.ru 3011
ns4-cloud.nic.ru 2938
ns8-cloud.nic.ru 2913
dns9.hichina.com 2874
dns10.hichina.com 2874
ns65.domaincontrol.com 2828
ns66.domaincontrol.com 2825
ns09.domaincontrol.com 2781
ns10.domaincontrol.com 2773
dns.fastdns24.com 2710
dns3.fastdns24.eu 2696
dns2.fastdns24.org 2695
dns4.fastdns24.link 2694
ns3-l2.nic.ru 2682
ns3.timeweb.org 2589
ns2.timeweb.ru 2589
ns1.timeweb.ru 2589
ns4.timeweb.org 2588
dns.home.pl 2508
dns2.home.pl 2508
dns3.home.pl 2507
ns51.domaincontrol.com 2421
ns52.domaincontrol.com 2411
ns02.domaincontrol.com 2389
ns01.domaincontrol.com 2389
ns2.namespace4you.de 2272
ns.namespace4you.de 2272
ns2.value-domain.com 2264
ns1.value-domain.com 2263
dns1.namecheaphosting.com 2188
dns2.namecheaphosting.com 2187

Pełną listę popularności serwerów znajdziemy tutaj.

Czy około od 5% do 10% “wadliwych” konfiguracji serwerów DNS na 1 milion domen to dużo, czy mało? Pozostawiam do oceny Czytelnikowi.

Więcej informacji: List of Internet top-level domains, How to Download a List of All Registered Domain Names

Kategorie K a t e g o r i e : Bezpieczeństwo

Tagi T a g i : , , , , , ,

2 komentarze.

  1. Bardzo dobry pomysł rozszerzający powyższy test zaproponował Błażej Miga: aby oprócz sprawdzania serwerów NS zwracanych oficjalnie dla domeny – sprawdzać dodatkowo serwer znajdujący się w rekordzie SOA, w którym znajdują się zupełnie inne / zapomniane serwery DNS.

  2. Nowe źródło domen od Cisco Umbrella.