Gelin Tcpdump ile DNS sorgularını filtreleyelim
Tcpdump
kaç defa hayatımı kurtardı sayısını hatırlamıyorum. Daha çok klasik
port, host ya da protokol üzerinden trafik kontrolü için kullanıyorum ama geçenlerde
karşılaştığım bir sorun sebebiyle daha ileri seviye filtreleme kullanmak zorunda kaldım.
Biraz da faydalı olacağını düşündüğüm ve egzersiz olsun diye izlediğim adımları paylaşmak istedim.
Sorunumuz neydi?
Canlı ortamda bizim sistemimizden ortamındaki sunucuların bazılarına ulaşamadığını
gördüm, sunuculara ulaşmamız için DNS
sunucusundan isim ile çözümleme yapması gerekiyor.
Bu yüzden DNS
sunucusuna hostname
bilgisinden IPv4
çözmek için atılan A
tipi sorguları analiz etmem gerekti.
Tshark
ya da Wireshark
gibi araçlarla aynı iş daha kolay yapılabilir ama unutmayın canlı ortamda çalışan bir
sistem var ve elimizde olan minimum araçla, yeni talep, yükleme değişiklik yapmadan
problemi tespit etmemiz lazım. Bu sunucuda yüklü olan bu işe en uygun araç tcpdump
ile
işe koyulalım.
Birinci deneme
DNS bildiğiniz gibi UDP protokolü üzerinden çalışıyor ve varsayılan olarak 53 portunu kullanıyor. Öncelikle ilgilendiğim network arayüzü üzerinden geçen DNS trafiğine bakmak için aşağıdaki gibi bir filtre kullandım.
tcpdump -ln -i ens160 'udp port 53'
10:10:28.000080 IP 192.168.100.169.62567 > 192.168.100.20.53: 21950+ SRV? _ldap._tcp.Default-First-Site-Name._sites.server.local. (73)
10:10:28.008770 IP 192.168.100.30.52098 > 192.168.100.20.53: 23304+ AAAA? hooks.slack.com.server.local. (47)
10:10:28.008770 IP 192.168.100.30.38794 > 192.168.100.20.53: 3665+ A? hooks.slack.com.server.local. (47)
10:10:28.158962 IP 192.168.100.32.43673 > 192.168.100.20.53: 9534+ A? arbiter1. (26)
10:10:28.230193 IP 192.168.100.22.61610 > 192.168.100.20.53: 24725+ A? au.download.windowsupdate.com. (47)
10:10:28.230454 IP 192.168.100.22.61610 > 192.168.100.21.53: 24725+ A? au.download.windowsupdate.com. (47)
10:10:28.702316 IP 192.168.100.21.57615 > 192.168.100.20.53: 40774+ SOA? server.local. (31)
10:10:28.954151 IP 192.168.100.133.58060 > 192.168.100.20.53: 37885+ AAAA? hooks.slack.com. (33)
10:10:28.954392 IP 192.168.100.133.54569 > 192.168.100.20.53: 19301+ A? hooks.slack.com. (33)
10:10:29.153919 IP 192.168.100.21.56348 > 192.168.100.20.53: 38375+ A? download.windowsupdate.com. (44)
10:10:29.415216 IP 192.168.100.21.54360 > 13.107.236.205.53: 19731 [1au] A? download.windowsupdate.com. (55)
10:10:29.454522 IP 13.107.236.205.53 > 192.168.100.21.54360: 19731*- 1/0/1 CNAME wu-fg-shim.trafficmanager.net. (98)
10:10:29.455627 IP 192.168.100.21.53126 > 192.168.100.20.53: 55492+ A? wu-fg-shim.trafficmanager.NET. (47)
10:10:29.600610 IP 192.168.100.33.53418 > 192.168.100.20.53: 52069+ A? arbiter1. (26)
10:10:29.703074 IP 192.168.100.21.57615 > 192.168.100.20.53: 40774+ SOA? server.local. (31)
Bu filtre sonucunda 53 portu üzerinden geçen tüm trafiği yukarıdaki gibi görebiliyoruz fakat
görüldüğü gibi bulmak istediğimiz kayıtlar dışında SRV,AAAA,CNAME..
gibi tüm DNS mesajlarını görüyoruz.
Fakat bizim istediğimiz sadece hangi adresler için Type A DNS Query
sorgularının yapıldığını tespit etmek.
UDP Paket Yapısı
Bunu yapmak için daha ileri seviye bir filtre yazmak dolayısıyla protokol detaylarına bakmamız gerekir. İlk olarak bilmemiz gereken DNS UDP üzerinde çalışan bir protokol ve bunu dikkate alarak bahsettiğimiz tipte DNS sorgularını nasıl bulabiliriz bulmaya çalışalım. DNS protokol başlık yapısı aşağıda görülebilir.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-----------------------------------------------------------------
| Source Port | Destination Port |
-----------------------------------------------------------------
| Length | Checksum |
-----------------------------------------------------------------
| |
| Data |
| |
-----------------------------------------------------------------
Data
kısmının üstündeki satırlar protokol başlığını gösteriyor. Başlığı incelediğimizde 8 byte
uzunluğunda olduğunu ve içerdiği alanları görebiliyoruz.
DNS
paketleri ise Data
kısmının içerisinde geliyor, bu yüzden DNS
paket yapısına da bakmamız gerekiyor.
DNS Paket Yapısı
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
---------------------------------
| ID |
---------------------------------
| Flags |
---------------------------------
| QDCOUNT |
---------------------------------
| ANCOUNT |
---------------------------------
| NSCOUNT |
---------------------------------
| ARCOUNT |
---------------------------------
DNS paketinin yapısına baktığımızda her biri 2 byte
yer tutan 6 alan görüyoruz. Bu paket UDP paketi içinde Data
kısmında gönderilmiş olacak.
Bizim bulmamız gereken iki kısım var, birincisi Flags
alanı içeriğinden sadece Query
tipinde olan paketleri bulmamız lazım.
Daha iyi gözümüzde canlansın diye, DNS trafiği bulunan bir pcap
dosyasını Wireshark
üzerinde inceleyelim.
İkinci Deneme
Yukarıdaki resim bizim bulmak istediğimiz Type A DNS Query
ve resme baktığımızda ID
alanının hemen altında olan Flags
içinde
hangi bit
değerinin 1
olarak gönderildiğini görebiliyoruz. Yanda da Flags
alanının gönderilmiş değerinin 0x0100
olduğu görülüyor.
O zaman şöyle yapabiliriz, UDP paket başlığı 8 byte
yer kaplıyor demiştik, üstüne 2 byte
DNS paketindeki ID
alanı geldi, hemen sonrasında
gelen Flags
alanı aslında udp[10]
içinde bulunması gerekiyor (dizi 0 ile başlıyor). Buradan şöyle bir filtre yazarak ilgili paketleri bulabiliriz
diye düşünüyorum.
tcpdump -ln -i ens160 'udp port 53 and udp[10] & 0x80 = 0'
tcpdump -ln -i ens160 'udp port 53 and udp[10] = 0x1'
tcpdump -ln -i ens160 'udp port 53 and udp[10] = 1'
Yukarıdaki filtrelerin hepsi aynı kapıya çıkıyor. & 0x80 = 0
nereden geldi diye sorulabilir, hemen açıklayalım. Tcpdump ile istersek bit flag
karşılaştırması yaparak
için aslında dolaylı yoldan oradaki değerin 0x0100
olduğunu kontrol edebiliriz. Yukarıdaki filtrelerden herhangi birini denediğimizde sonuç aşağıdaki gibi oluyor
08:50:00.092381 IP 192.168.100.61.4045 > 208.91.112.52.53: 43+ A? strict.bing.com. (33)
08:50:00.280419 IP 192.168.100.111.52874 > 192.168.100.20.53: 5935+ SRV? _ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.server.local. (83)
08:50:04.723430 IP 192.168.100.33.55386 > 192.168.100.20.53: 64594+ A? arbiter1. (26)
08:50:04.913121 IP 192.168.100.33.58165 > 192.168.100.20.53: 5767+ [1au] A? mongo03. (36)
08:50:04.913177 IP 192.168.100.33.53484 > 192.168.100.20.53: 54515+ [1au] AAAA? mongo03. (36)
08:50:04.914712 IP 192.168.100.33.44411 > 192.168.100.20.53: 44196+ [1au] A? mongo03. (36)
08:50:04.916585 IP 192.168.100.33.50264 > 192.168.100.20.53: 59818+ [1au] AAAA? arbiter1. (37)
08:50:04.916739 IP 192.168.100.33.36527 > 192.168.100.20.53: 52768+ [1au] A? arbiter1. (37)
08:50:04.918118 IP 192.168.100.33.36505 > 192.168.100.20.53: 1722+ [1au] A? arbiter1. (37)
08:50:04.918399 IP 192.168.100.33.59246 > 192.168.100.20.53: 19402+ [1au] AAAA? arbiter1. (37)
08:50:04.919751 IP 192.168.100.33.40935 > 192.168.100.20.53: 8112+ [1au] A? arbiter1.server.local. (51)
08:50:05.027957 IP 192.168.100.32.36793 > 192.168.100.20.53: 39115+ A? arbiter1. (26)
08:50:05.042618 IP 192.168.100.61.4045 > 208.91.112.52.53: 49402+ A? swscan.apple.com. (34)
Yukarıdaki paketlere baktığımızda ilk aşamada karşımıza çıkan DNS sorgu cevaplarının gittiğini görüyoruz ama hala listede
AAAA, SRV
gibi DNS sorgu istekleri görülüyor. Bunlardan kurtulmak için filtreyi son defa revize etmeye çalışalım.
Son Deneme ve Başarı
DNS paket başlığının yapısını incelediğimizde, ilgili sorgu tipinin paketinin en sondan iki önceki 2 byte
içinde tutulduğunu görebiliyoruz.
Aşağıdaki resimden daha iyi anlaşılabilir.
Yani yapmak bulmak istediğimiz filtreleme kabaca şu şekilde olması gerekiyor.
udp[paketuzunlugu-4:2] = 0x0001
[a:b]
notasyonu a numaralı bytedan başlayarakb
kadar byte al demek.
Yukarıdan hatırlayacak olursanız UDP paket uzunluğu kendi başlığı içerisinde 2 byte
olarak 4:2
arasında tutuluyor.
Bu bilgiyi kullanarak tekrar filtreyi tekrar revize edelim.
tcpdump -ln -i ens160 'udp port 53 and udp[10] & 0x80 = 0 and udp[(udp[4:2]-4):2] = 0x0001'
09:37:26.521212 IP 192.168.100.32.50375 > 192.168.100.20.53: 57051+ A? arbiter1. (26)
09:37:26.522193 IP 192.168.100.32.35151 > 192.168.100.20.53: 56503+ A? arbiter1.server.local. (40)
09:37:26.523585 IP 192.168.100.32.34443 > 192.168.100.20.53: 53150+ A? arbiter1. (26)
09:37:26.524553 IP 192.168.100.32.40833 > 192.168.100.20.53: 53150+ A? arbiter1. (26)
09:37:26.525578 IP 192.168.100.32.46435 > 192.168.100.20.53: 51829+ A? arbiter1.server.local. (40)
09:37:26.727766 IP 192.168.100.33.60701 > 192.168.100.20.53: 60216+ A? arbiter1. (26)
09:37:26.729244 IP 192.168.100.33.48042 > 192.168.100.20.53: 60216+ A? arbiter1. (26)
09:37:26.730305 IP 192.168.100.33.35631 > 192.168.100.20.53: 58112+ A? arbiter1.server.local. (40)
09:37:27.087333 IP 192.168.100.32.48370 > 192.168.100.20.53: 25327+ A? arbiter1. (26)
09:37:27.088618 IP 192.168.100.32.37493 > 192.168.100.20.53: 25327+ A? arbiter1. (26)
09:37:27.089651 IP 192.168.100.32.50450 > 192.168.100.20.53: 21119+ A? arbiter1.server.local. (40)
Biraz zahmetli oldu ama görüldüğü gibi işe yaradı ve sadece A tipi DNS sorgularını filtreledik.
Daha Basit Bir Yöntem
Yukarıda kullandığımız yöntem ilgili paketleri direk tcpdump
filtreleriyle bulmaktı. Bunun yerine tabi
aşağıdaki gibi klasik unix
araçlarından awk
kullanarak basit bir işlemle de işimizi tamamlayabilirdik.
tcpdump -ln -i ens160 "udp port 53" | awk '/A\?/{adr = $(NF-1); if(!d[adr]) { print adr; d[adr]=1; fflush(stdout) } }'
Aslında yaptığımız tüm sonuçları tcpdump
üzerinden alıp ilgilendiklerimizi awk
ile filtrelemek. Basit senaryolar
için bu tarz bir yöntem kullanılabilir fakat tcpdump
filtreleri arka planda BPF kullandığı ve
burada kernel
seviyesinde bir filtreleme olduğu için performans konusunda oldukça farklılık olacaktır.
Bonus
Hangi adres için kaç tane DNS sorgusu atılmış canlı olarak güncellenen şekilde görmek için aşağıdaki awk
scriptini hazırladım.
tcpdump -ln -i ens160 'udp port 53 and udp[10] & 0x80 = 0 and udp[(udp[4:2]-4):2] = 0x0001' | awk '
{
adr=$(NF-1);
dict[adr]++;
system("clear")
system("tput cup 0 0")
for (key in dict)
print dict[key] " : " key
}
'
6 : swscan.apple.com.
12 : mongo03.
1 : play.google.com.
3 : autoupdate.opera.com.
1 : strict.bing.com.
208 : arbiter1.server.local.
1 : remote-host.server.local.
482 : arbiter1.
2 : remote-host.
1 : settings-win.data.microsoft.com.
1 : wpad.server.local.
Çalıştırdığımızda aşağıdakine benzer sonuçları görebilirsiniz. Gerisi hayal gücünüze kalmış