Bildiğiniz gibi TCP, İnternet'in temel protokollerinden biridir. IETF tarafından yayınlanan RFC'ye göre, işletim sistemlerinde implementasyon edilir. Fakat duruma göre bazı farklı uygulama yöntemleri olabilir.
TCP protokolünde hedef ile kurban arasındaki trafiğe veri eklemek (aradaki adam saldırısı, man in the middle) için genellikle aynı ağda olmak gerekir. Bunun için ARP zehirlenmesi oldukça kullanılan bir yöntemdir. Peki aynı ağda olmadan, çok uzaklardan hedef ile kurban arasındaki trafik tespit edip, veri enjekte edilebilir mi? Cevaba şaşıracaksınız ama cevabı evet. Bu tür saldırılara da Off-Path Injection saldırıları deniyor.
Siber saldırganların, TCP trafiğine veri eklemelerini sağlayan Linux işletim sistemlerindeki TCP implementasyonundan kaynaklı 2016 senesinde bir zafiyet yayınlandı. Bu yazı da hem Linux çekirdeği 3.5 ve daha önceki sürümlerde (CVE-2016-5696) olan bir zafiyeti inceleyeceğiz hem de diğer off-path injection saldırıları için bilgiler paylaşacağız.
Örnek Off-Path Injection (Türkçesi için yardım istiyorum, bulamadım) Saldırıları
DNS Önbellek Zehirlenmesi (cache poisining) : DNS sunucularında isim (alan adı, siberkuvvet.com gibi) ve IP eşleşmesi (0.0.0.1 olsun) tutulur. Eğer bir DNS sunucusu kendisinde olmayan veriyi başka bir DNS sunucusundan isterse bu veri bir süre önbellekte tutulur. Saldırganlar bir şekilde DNS sunucusunun önbelleğini zehirlerse yani hedef alan adı için kendi IP adresini (0.0.0.2 olsun) girerse trafiği ele geçirebilir. Çünkü kurban gitmek istediği alan adının gerçek IP adresi yerine (0.0.0.1) saldırganın yönettiği bir IP adresine (0.0.0.2) yönlenecektir.
Tahmin Edilebilir (Predictable) Initial Sequence Number (ISN) : TCP’de her paket özel bir değer (sequence number) ile iletilir. Bu değer hem alıcı hem de verici tarafından tam olarak doğru olması şartıyla kabul edilir. Bu nedenle TCP protokolünde IP adresi sahte iletilse bile bu değer doğru tahmin edilemediği için saldırılar başarılı olamaz. Fakat 1995 öncesine kadar bu böyle değildi. Kevin Mitnick, 1995’de bu değeri tahmin ederek uzaktan bir saldırı gerçekleşti ve sonrasında TCP’de bulunan (implementasyonu) bu zafiyet kapatıldı. Eğer doğru implementasyon kullanmayan sistemler varsa bu zafiyet hala bulunabilir. Örneğin burada olduğu gibi: https://vulners.com/openvas/OPENVAS:1361412562310103701
TCP Reset Saldırısı : TCP paketlerinde bağlantıyı düşürmek için bir bitin 1 olması yeterli (RST flag). Bunun için bir önceki saldırıdaki gibi Sequence Number’ın tam olarak bilinmesine gerek yok. Bunun yerine belirli bir aralıkta olması yeterli. Bu sayede saldırgan daha önceden bildiği hedef ve kurbanın IP adreslerine sahte TCP Rst paketleri göndererek bağlantıyı düşürebilir. RFC 5961’de bu değişti (2010 senesi). Eğer TCP paketinde gelen sequence number tam aranan değer değilse tekrar istenmesi için bir ACK biti atanmış bir paket gönderiliyor. Bu sayede bağlantıyı kapatmak yerine paket tekrar isteniyor.
Bizim detaylı inceleyeceğimiz ise Linux kernel’inde global rate olarak tanımlanan ACK sayısından kaynaklı bir side channel zafiyeti. İlgili makalenin başlığı ise “Off-Path TCP Exploits: Global Rate Limit Considered Dangerous”. Makale hakkında bağlantıları aşağıda bulabilirsiniz.
Linux Kernel Global Rate Limit Off-Path Zafiyeti - CVE-2016-5696 İncelemesi
Zafiyeti anlamadan önce RFC-5961 ve öncesi diye ikiye ayıralım. RFC-5961 bazı TCP implementasyonundan kaynaklı zafiyetleri gidermek için yayınlandı, ve malesef bizim inceleyeceğimiz zafiyetlere yol açtı.
RFC-5961 öncesinde eğer alıcı taraf, yukarıda bahsettiğimiz sequence number değeri geçerli aralıkta olan bir SYN paketi alırsa otomatik olarak bağlantıyı resetliyordu. Eğer geçerli aralıkta değilse geriye bir ACK gönderiyordu.
RFC-5961’de ise, eğer alıcı bir SYN paketi alırsa, geriye otomatik olarak ACK dönüyor. Eğer gönderici ACK paketini alırsa RST paketi ile cevap veriyor ve bağlantı ondan sonra resetleniyor. Eğer RST paketi hiç alınmazsa, ilk gelen SYN paketi göz ardı ediliyor.
Ayrıca RST paket implementasyonu da değişti. RFC-5961 öncesinde yukarıda bahsettiğimiz gibi eğer sequence number geçerli bir aralıkta RST paketi alınırsa bağlantı resetleniyordu. RFC-5961 ile tam değerinde göndermesi gerekli oldu. Yeni RFC’ye göre eğer pakette sequence number belirli aralıkta değilse geriye ACK paketi (buna challenge ACK paketi diyelim) gönderiliyor. O zaman soru şu saldırgan sürekli olarak sequence number’ı belirli aralıkta paket gönderirse sunucu tarafından sürekli ACK paketi iletirse ortalık karışmaz mı ?
Evet karışır. Bu nedenle RFC-5961 diyor ki: Bir ACK azaltma mekanizması (ACK throttling mechanism) kurulması lazım. Yeni belirli bir zaman içerisinde ne kadar ACK paketi göndereceğinin ayarlanması lazım. Linux kernelinde bu implementesyon sysctl_tcp_challenge_ack_limit adında bir değişkende tutuluyor ve varsayılan olarak 100 değeri var içerisinde. Yani global olarak, belirli zamanda (1 saniye) ancak 100 adet challenge ACK paketleri gönderilebiliyor. Bu da zafiyete yol açıyor. Zafiyet içeren kernel kodunu buldum, aşağıda paylaşıyorum.
Nasıl olduğuna bakalım.
Zafiyet Detayı ve Sömürülmesi
Senaryomuz şu şekilde. Evinizde oturuyor ve bir haber sitesinden haber okuyorsunuz. Bambaşka bir ülkede bir siber saldırgan, yukarıdaki zafiyeti kullanarak sizinle haber sitesinin arasında giriyor ve haber sitesinden dönen cevaba zararlı kod enjekte ediyor (yalnızca sizin bağlantınıza). . Yeni hedef sunucu, kurban da sizin bilgisayarınız.
Kaynak Portu Öğrenme
Öncelikle saldırgan hedef IP, kurban IP adresini biliyor olması lazım.
Aşağıdaki resimde çizgili istekler sahte istekleri, düz çizgiler ise gerçek istekleri gösteriyor.
Şimdi ise devreye çok basit bir mantık giriyor. Bir saniye içerisinde şunları yapıyoruz;
- Öncelikle hedefe (sunucuya) kurbanın IP adresinden geliyormuş gibi paket gönderiyoruz. Fakat source port değerlerini kendimiz yönetiyoruz. Burada tek bir paket gönderiyoruz.
- Eğer source port doğru ise sunucu gerçekten bir challenge ack değerini kurbana (size) gönderiyor.
- Hemen ardından gerçekten 100 RST paketi gönderiyoruz sunucuya.
- Hatırlatma : Hala bir saniyenin içerisindeyiz. Bu nedenle sunucu en fazla 100 ACK gönderebilir.
- Eğer 100 RST paketinde 99 challenge ACK geldiyse, demek ki ilk gönderdiğimiz source port değeri doğru.
- Eğer 100 RST paketinde 100 challenge ACK geldiyse, değerler yanlış değiştirip tekrar yapıyoruz.
- Bu yöntem ile source port en kötü 1 senaryoda bir saat içerisinde bulunabiliyormuş. Zaten maksiumum 65536 olsa da Linux kernelleri 32768 to 61000 aralığını kullanıyor source-port için.
- Saldırganın elinde şu an, hedef IP, hedef port, kaynak IP ve kaynak port. Sıra diğer saldırıda.
Sequence Number Değerini Öğrenme
Mantık aynı kaynak portu biliyoruz ve saldırıya devam ediyoruz.
- Sunucuya (hedefe) 1 adet RST paketi gönderiyoruz, bu pakette kurbanın IP adresi ve kaynak portu olarak daha önceden bulduğumuz değerleri koyuyoruz. Seq number’ı ise sürekli değiştiriyoruz.
- Eğer seq number geçerli aralıkta ise (in window), gerçek kurbana (sizin bilgisayara), sunucu tarafından bir challenge ack gidiyor.
- Sonra 100 RST paketi gönderiyoruz ve dönen cevabı inceliyoruz.
- Eğer 99 ise ilk gönderdiğimiz seq number doğru, değilse değiştirip başa alıyoruz.
- Tüm bunları bir saniye de yapıyoruz.
ACK Number Bulma
TCP’de gönderilen veriler ACK number değerine göre paylaşılır, her pakette paketin içeriğine göre atar veya azalır. Bunu açıklamıyorum aynı mantık bulmak için resim aşağıdaki gibi.
Sonuç
Ve saldırgan uzaktan, TCP bağlantısına veri enjekte etmek için her şeye sahip olmuş oluyor. Sonrasında ise aşağıdaki video da görebileceğiniz gibi hedefin bağlantısına veri yerleştirebilyorlar. Bu saldırıda birden fazla sorun var örneğin sunucu ve saldırganın zamanının sekron olması gibi. İsteyenler makaleden devamını okuyabilir.
Zafiyetin giderildiği kernek kod aşağıda:
Github’da bir de demo için kodlar var. Kendim bir demo yaptım gayet başarılı.
Farklı bir saldırı ve side-channel zafiyeti olduğu için sizinle paylaşmak istedim, umarım beğenirsiniz.
Bu makale 0xnur tarafından yazılmış veya yayınlanmıştır.