TCP el sıkışması
- TCP'nin üçlü el sıkışmasını, bayraklarını ve bağlantı durumlarını paket yakalayarak okur, kapalı ile filtreli portu ayırır, SYN selini tanırsın.
- SYN-ACK açık portu, RST kapalı portu, sessizlik ise filtreyi gösterir. UDP bu anlaşmayı hiç kurmaz.
- Bu derste sıra numaralarını hesaplar, tcpdump ve ss ile bağlantıyı izler, SYN seline karşı savunmayı görürsün.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +55 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
TCP (Transmission Control Protocol, iletim denetim protokolü) veriyi sıralı ve kayıpsız taşıma sözü verir. Her bayta bir sıra numarası verir, karşı taraf aldığını onaylar ve kaybolan parçayı yeniden ister. Ağ tıkandığında hızını da kendiliğinden düşürür. Web sayfası, SSH oturumu ve veritabanı sorgusu bu sözün üstünde akar.
Sözün bir bedeli vardır: veri akmadan önce iki taraf birbirini tanımak zorundadır. Her iki uç, karşının başlangıç sıra numarasını öğrenmeden ilk baytı yollayamaz. Bu tanışmanın adı üçlü el sıkışmadır ve her yeni bağlantının başında bir kez yaşanır.
Üç paketlik anlaşma
Birinci pakette istemci SYN bayrağı taşıyan bir paket yollar ve kendi başlangıç sıra numarasını bildirir. İkinci pakette sunucu SYN ile ACK bayraklarını birlikte taşıyan SYN-ACK döner. Bu tek pakette hem istemcinin numarasını onaylar hem kendi başlangıç numarasını yollar.
Üçüncü pakette istemci ACK yollar ve sunucunun numarasını onaylar. SYN bayrağı bir sıra numarası tüketir, onay değeri bu yüzden karşının numarasının bir fazlasıdır. RFC 9293 başlangıç numarasının tahmin edilemez seçilmesini ister, çünkü tahmin edilebilen numara sahte bağlantı kurma denemesine kapı açar.
Aşağıdaki yakalamada istemci 1000 numarasıyla başlar. Sunucu ack 1001 yazarak 1000'i aldığını ve sıradaki numarayı beklediğini söyler. Aynı pakette kendi numarası olarak 5000'i yollar. İstemci de ack 5001 ile cevap verir ve anlaşma tamamlanır.
İki yönün numaraları birbirinden bağımsızdır, her yön kendi akışını ayrı sayar. Gerçek sistemler başlangıç numarasını rastgele seçer; buradaki yuvarlak sayılar okumayı kolaylaştırır. Yakalamayı sudo tcpdump -nS ile alırsın: -n adları çözmez, -S sıra numaralarını mutlak yazar, -i ise dinlenecek arayüzü seçer. Çıktıdaki [S] SYN'i, [S.] SYN-ACK'i, tek başına [.] ise ACK'i gösterir, çünkü tcpdump nokta işaretini ACK bayrağı için kullanır.
$ sudo tcpdump -nS -i eth0 'tcp and host 10.0.0.5 and port 443' 10:02:11.114 IP 10.0.0.9.51544 > 10.0.0.5.443: Flags [S], seq 1000 10:02:11.126 IP 10.0.0.5.443 > 10.0.0.9.51544: Flags [S.], seq 5000, ack 1001 10:02:11.126 IP 10.0.0.9.51544 > 10.0.0.5.443: Flags [.], ack 5001
Çıktıyı satır satır oku. İlk satırda 10.0.0.9 adresli istemci, 51544 numaralı geçici portundan sunucunun 443 portuna SYN yolluyor. İkinci satırda sunucu [S.] ile cevap veriyor.
Üçüncü satırda istemci yalnız ACK bayrağıyla cevap veriyor ve bağlantı kuruluyor. İlk veri baytı ancak bu noktadan sonra yola çıkar. İlk iki paket arasındaki 12 milisaniye, bu bağlantının gidiş dönüş süresidir (RTT). El sıkışma her yeni bağlantıya bu kadar gecikme ekler.
Kalabalık bir sunucuda yalnız açılışları görmek istersen yakalama süzgecini daraltırsın. tcp[tcpflags] & tcp-syn != 0 ifadesi, SYN bayrağı yanan bütün paketleri yakalar. Grafik arayüz istersen Wireshark'ta aynı bağlantıya sağ tıklayıp Follow TCP Stream dersin ve dağınık paketleri tek bir konuşma olarak okursun.
Etkinlik 1 / 7
İstemci SYN paketinde seq=7200 yolladı. Sunucunun SYN-ACK paketindeki onay değeri ack=____ olur.
Bayraklar ve durum makinesi
TCP başlığındaki denetim bitleri, yani bayraklar, paketin niyetini söyler. RFC 9293 sekiz bayrak sayar ve altısı ilk tasarımdan gelir. SYN yeni bir bağlantı açar ve sıra numarasını başlatır.
ACK bir onay taşır, kurulmuş bağlantıda neredeyse her pakette yanar. FIN veriyi bitirip düzgün kapanışı başlatır, RST ise bağlantıyı aniden koparır. PSH verinin tamponda bekletilmeden uygulamaya iletilmesini ister.
URG acil veriyi işaret eder ama bugün neredeyse hiç kullanılmaz. Kalan ikisi, CWR ve ECE, sonradan tıkanıklık bildirimi (ECN) için eklendi. Tek başına SYN yeni bir bağlantı denemesi, SYN ile ACK bir karşılık, tek başına ACK ise akan bir konuşmadır.
Wireshark'ta bayraklara göre süzmek için görüntüleme süzgeci yazarsın. tcp.flags.syn == 1 && tcp.flags.ack == 0 yalnız bağlantı açma denemelerini gösterir. tcp.flags.reset == 1 ise koparılan bağlantıları listeler. Bu dil tcpdump'ın yakalama süzgecinden farklıdır.
Yakalama süzgeci kayda neyin gireceğine karar verir ve kayıt başlamadan yazılır. Görüntüleme süzgeci ise kaydedilmiş paketlerden hangilerini göreceğini seçer, kaydı değiştirmez. Yakalamayı fazla daraltırsan sonradan ihtiyaç duyduğun paket hiç kaydedilmemiş olur. Olay incelemesinde bu yüzden geniş yakalar, dar görüntülersin.
TCP her bağlantıyı bir durum makinesiyle izler. Sunucudaki dinleyen soket LISTEN durumunda bekler. SYN gelince bağlantı SYN-RECV durumuna, el sıkışma bitince ESTABLISHED durumuna geçer. İstemci ise ilk SYN'i yolladıktan sonra SYN-SENT durumunda cevap bekler.
Kapanışta ilk FIN'i yollayan taraf FIN-WAIT durumlarından geçer ve en sonunda TIME-WAIT durumunda bir süre bekler. FIN'i alan taraf CLOSE-WAIT durumuna girer ve kendi uygulaması soketi kapatana kadar orada kalır. Bu durumları ss -tan komutuyla canlı görürsün: -t TCP'yi, -a bütün soketleri, -n sayısal adresleri seçer.
$ ss -tan State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 511 0.0.0.0:443 0.0.0.0:* ESTAB 0 0 10.0.0.5:443 10.0.0.9:51544 SYN-RECV 0 0 10.0.0.5:443 192.0.2.77:40112 TIME-WAIT 0 0 10.0.0.5:443 10.0.0.9:51502 CLOSE-WAIT 1 0 10.0.0.5:39822 10.0.0.7:5432
Çıktıyı satır satır oku. İlk satır 443 portunda bekleyen web sunucusudur. ESTAB satırı 10.0.0.9 ile kurulu, veri akan bir bağlantıyı gösterir. SYN-RECV satırında 192.0.2.77 adresi SYN yollamış ama el sıkışmayı henüz tamamlamamıştır.
TIME-WAIT satırı kapanmış ve kısa süre bekletilen bir bağlantıdır, bu normaldir. Son satır dikkat ister. CLOSE-WAIT'te takılan bağlantı, karşı tarafın kapattığını ama uygulamanın kendi soketini kapatmadığını gösterir. Bu satırlardan yüzlercesi birikirse uygulamada soket sızıntısı vardır ve sunucu zamanla yeni bağlantı açamaz.
Derinleş:RFC 793'ten RFC 9293'e ve TIME-WAIT
TCP'nin ilk tanımı 1981 tarihli RFC 793'tür. Kırk yıl boyunca onlarca RFC bu belgeyi düzeltti ve genişletti. RFC 9293, 2022'de bu parçaları tek belgede topladı ve RFC 793'ün yerini aldı. Belgenin en çok soru doğuran ayrıntılarından biri TIME-WAIT süresidir.
Kapanışı başlatan uç, bağlantıyı azami segment ömrünün (MSL) iki katı kadar bekletir. RFC 9293 MSL için iki dakika varsayar, bu da dört dakikalık bir bekleme demektir. Linux ise bu süreyi sabit 60 saniye tutar. Bekleme iki işe yarar.
Son ACK kaybolursa karşı taraf FIN'i yeniden yollar ve bekleyen uç ona yine cevap verebilir. Ayrıca ağda gecikmiş eski paketler, aynı adres ve port ikilisiyle açılan yeni bir bağlantıya karışmaz. Yoğun bir istemcide binlerce TIME-WAIT görmek bu yüzden çoğu zaman normaldir. Asıl uyarı işareti, sayısı durmadan artan CLOSE-WAIT kayıtlarıdır.
Etkinlik 2 / 7
ss çıktısında gördüğün durumu, bağlantının hangi aşamasında olduğuna göre ayır.
Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.
0/6 seçenek yerleşti.
Kapalı port, filtre ve UDP
Bağlantı kapanırken iki taraf ayrı ayrı FIN yollar ve ayrı ayrı onaylar, yani düzgün kapanış çoğu zaman dört pakettir. Ani kapanışın yolu ise RST'dir. RFC 9293'e göre kapalı bir porta SYN gelirse işletim sistemi RST ile cevap verir. Gelen SYN'de ACK olmadığı için bu cevapta RST ile ACK bayrakları birlikte yanar ve tcpdump bunu [R.] diye yazar.
RST hedefin ayakta olduğunu da kanıtlar, çünkü karşıda cevap veren bir makine vardır. Güvenlik duvarı ise çoğu zaman hiç cevap vermez ve paketi sessizce düşürür. Bazı duvarlar sessiz kalmak yerine ICMP ulaşılamaz mesajı döner. Bir porta SYN attığında böylece üç ayrı cevapla karşılaşırsın.
SYN-ACK dinleyen bir servisi kanıtlar. RST portun kapalı ama makinenin ulaşılabilir olduğunu gösterir. Sessizlik ya da ICMP hatası ise araya giren bir filtreyi düşündürür.
UDP (User Datagram Protocol, kullanıcı veri birimi protokolü) bu anlaşmayı hiç kurmaz. Başlığı yalnız 8 bayttır: kaynak port, hedef port, uzunluk ve sağlama toplamı. Sıra numarası, onay ve yeniden gönderim yoktur. RFC 768 teslimatı garanti etmediğini açıkça yazar.
DNS, sesli görüşme ve oyunlar gecikmeyi kayıptan daha kötü gördüğü için UDP seçer. Kaybolan bir ses paketini yeniden istemek anlamsızdır, çünkü o anın sırası çoktan geçmiştir. HTTP/3'ün altındaki QUIC ise güvenilirliği UDP'nin üstüne kendisi kurar.
UDP'de kapalı port RST dönmez, çünkü ortada bir bağlantı yoktur. Onun yerine hedef ICMP port ulaşılamaz mesajı yollar, RFC 792'de bu mesaj tip 3, kod 3'tür. Açık bir UDP servisi ise anlamadığı pakete çoğu zaman hiç cevap vermez.
Sessizlik bu yüzden hem açık hem filtreli anlamına gelebilir, ikisini ayırmak zordur. UDP taraması bu belirsizlik yüzünden hem yavaş hem de yanılabilir. Bir UDP portunu doğru anlamak için çoğu zaman servise ait gerçek bir sorgu yollaman gerekir, örneğin 53 portuna bir DNS sorusu.
Etkinlik 3 / 7
Kapalı bir porta SYN paketi yolladın. Cevaptaki flags alanında hangi bayraklar görünür?
Önce kodu oku, sonra çıktıyı seç.
$ hping3 -S -p 8080 10.0.0.5 len=46 ip=10.0.0.5 flags=?? seq=0
Etkinlik 4 / 7
Doğru mu, yanlış mı?
Kapalı bir UDP portu, gelen pakete TCP'deki gibi RST paketiyle cevap verir.
Yoklama, SYN seli ve savunma
Bir TCP portunu elle yoklamak için netcat (nc) kullanırsın. -z bayrağı bağlantıyı kurar ve hiç veri yollamadan kapatır. -v bayrağı sonucu ekrana yazar. -w 3 ise bekleme süresini üç saniyeyle sınırlar.
Port açıksa succeeded sözcüğünü görürsün. RST gelirse Connection refused yazar.
Filtreli portta komut süre dolana kadar bekler ve timed out der. Bu üç sonuç, önceki bölümdeki üç cevabın komut satırındaki karşılığıdır. netcat tek bir portu hızla denemek için idealdir, çok sayıda port içinse tarayıcıya geçersin.
$ nc -zv -w 3 10.0.0.5 22 Connection to 10.0.0.5 22 port [tcp/ssh] succeeded! $ nc -zv -w 3 10.0.0.5 8080 nc: connect to 10.0.0.5 port 8080 (tcp) failed: Connection refused $ nc -zv -w 3 10.0.0.5 3306 nc: connect to 10.0.0.5 port 3306 (tcp) timed out: Operation now in progress
El sıkışmanın maliyeti kötüye de kullanılır. SYN selinde saldırgan, çoğu zaman sahte kaynak adresleriyle çok sayıda SYN yollar ve üçüncü paketi hiç göndermez. Sunucu her SYN için yarı açık bir kayıt tutar ve ACK bekler.
Kuyruk dolunca gerçek kullanıcıların bağlantısı da kurulamaz. MITRE ATT&CK bu davranışı T1499.001 OS Exhaustion Flood tekniği altında izler. Tespit için yarı açık bağlantıları sayarsın.
Sağlıklı bir sunucuda bu sayı birkaç tanedir, birden binlere fırlaması sel işaretidir. Akış kayıtlarında SYN sayısının ACK sayısını çok aşması da aynı şeyi söyler. Dağıtık sahte adreslerden gelen sel, kaynağa göre süzmeyi zorlaştırır.
$ ss -Htan state syn-recv | wc -l 3 $ ss -Htan state syn-recv | wc -l 1873 $ sysctl net.ipv4.tcp_syncookies net.ipv4.tcp_syncookies = 1
Önlemin temeli SYN çerezidir (SYN cookie). RFC 4987'ye göre sunucu, kuyruk dolduğunda bağlantı bilgisini kuyrukta tutmaz, onu SYN-ACK'in sıra numarasına kodlar. Geçerli ACK geri gelince bilgiyi o numaradan yeniden çıkarır ve kaydı ancak o zaman açar.
Linux'ta bu davranışı tcp_syncookies ayarı yönetir. Değer 1 ise çerez yalnız kuyruk taştığında devreye girer ve çoğu dağıtımda böyle gelir. Hız sınırı ve önde duran bir ters vekil (reverse proxy) de yükü emerek gerçek kullanıcıyı korur.
Etkinlik 5 / 7
10.0.0.5 makinesinin 443 portunu netcat ile, veri yollamadan ve sonucu ekrana yazdırarak yokla.
Etkinlik 6 / 7
SYN selinde saldırganın hiç yollamadığı, bu yüzden sunucudaki yarı açık kaydı bekleten paket hangisidir?
Diyagramda doğru kutuyu tıkla.
Maliyet: RTT, MSS ve pencere
Aynı hesabı performans tarafında da yaparsın. Her istekte yeni bir TCP bağlantısı açan uygulama, veriyi yollamadan önce el sıkışmayı beklemek zorundadır. Uzak bir veri merkezine bağlanan serviste bu bekleme, toplam gecikmenin gözle görülür bir parçasıdır.
İstanbul'daki bir uygulama Frankfurt'taki sunucuya bağlanırken her yeni bağlantı bir tam gidiş dönüş süresi kadar gecikir. Bağlantıyı yeniden kullanmak, yani açık tutup üstünden çok istek geçirmek, bu maliyeti büyük ölçüde ortadan kaldırır.
İki uç el sıkışırken pazarlık ettiği değerlerden biri MSS'tir (Maximum Segment Size, azami segment boyutu). MSS, tek bir TCP segmentine sığacak veri miktarını belirler ve genelde ağın MTU değerinden türer.
İkinci değer pencere boyutudur: karşının onay beklemeden kabul edeceği veri miktarı. Yüksek gecikmeli hatlarda küçük pencere hattı boş bırakır, bu yüzden pencere ölçekleme (window scaling) devreye girer. Canlı bir bağlantının bu değerlerini ss -ti komutuyla görür, gecikmeyi ve pencereyi oradan okursun.
$ ss -ti dst 10.0.0.5 ESTAB 0 0 10.0.0.9:51544 10.0.0.5:443 cubic wscale:7,7 rto:240 rtt:40.3/2.1 mss:1448 cwnd:10
İkinci satırı alan alan oku. mss:1448 değeri tek segmentteki en çok veriyi verir. Ethernet'in 1500 baytlık MTU'sundan 20 bayt IP ve 20 bayt TCP başlığı düşünce 1460 kalır. Zaman damgası seçeneği 12 bayt daha alır ve 1448'e iner.
wscale:7,7 iki yönün pencere ölçek çarpanıdır. rtt:40.3/2.1 ise ortalama gidiş dönüş süresini ve sapmasını milisaniye cinsinden verir. cwnd tıkanıklık penceresidir, yani onay beklemeden yola çıkabilecek segment sayısıdır. Yeniden gönderim olursa satıra retrans alanı da eklenir.
Derinleş:Pencere, gecikme ve bant genişliği
Bir bağlantının ne kadar hızlı akacağını yalnız bant genişliği değil, gecikme de belirler. Anahtar kavram bant genişliği gecikme çarpımıdır (bandwidth-delay product): yolda aynı anda durabilecek veri miktarı, bant genişliği ile gidiş dönüş süresinin çarpımına eşittir. 100 Mbit/s hızında ve 40 ms gecikmeli bir hatta bu çarpım 4 megabit, yani yaklaşık 500 kilobayttır. Pencere bu çarpımdan küçükse, gönderen bir tur veri yollar, sonra onay gelene kadar durur ve hattı boş bırakır.
Orijinal TCP başlığındaki pencere alanı yalnız 16 bittir, yani en çok 65535 baytı gösterebilir. RFC 7323 bu tavanı pencere ölçekleme seçeneğiyle kaldırır ve alanı bir çarpanla büyütür. Böylece kıtalar arası hızlı hatlar da dolar. Bir bağlantı beklenenden yavaşsa, suçlu her zaman bant genişliği değildir; çoğu zaman sorun pencere boyutu ile gecikmenin çarpımındadır.
Etkinlik 7 / 7
İstanbul'daki uygulama Frankfurt'taki sunucuya yeni bir TCP bağlantısı açıyor. Gidiş dönüş süresi 40 ms. İlk veri baytı, SYN yollandıktan en erken kaç ms sonra yola çıkar?
Tahminini kaydır, sonra gönder.
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.