Buffer Overflow Zaafiyeti Nedir ?

Burak Tahtacı
4 min readJan 2, 2021

--

Bilgisayar programları belirli bir görevi gerçekleştirmek için geliştirilmiş işlevsel araçlardır. Bu bir web sunucu programı yahut bir metin editörü programı olabilir. Bu programları kullanarak bilgisayarlarda çeşitli görevleri gerçekleştirebiliriz. Fakat işin karanlık boyutlarını düşündüğümüzde bu programları kullanan kişiler, programlardaki zaafiyetlerden yararlanarak programın olağan işleyişini değiştirip kendi istedikleri kod parçalarını sistemde çalıştırabilir hale gelebilirler.

Yani zararlı bir kullanıcı, programa öyle bir girdi (input) verir ki sistem artık olağan davranışını terk eder ve saldırganın kontrolüne geçer. İşte bu tarz saldırılara genel anlamda Code Execution / Command Injection saldırıları diyoruz. Örneğin bir web sunucu programı üzerinde kendi istediğiniz kodu çalıştırdığınızı düşünün; mesela example.com adresinde yer alan web sunucuda bir zafiyet keşfettiniz ve o zafiyeti kullanarak artık o bilgisayarda kendi yazdığınız komutları çalıştırabiliyor ve sistemi bütünüyle kontrol edebiliyorsunuz. Bu oldukça tehlikeli bir durum. Öyle ki otoriteler tarafından hala etkisi en yüksek saldırı tipi olarak nitelendirilmekte.

Peki saldırganlar bu kod çalıştırma işini nasıl yapıyorlar, bu duruma sebep olan güvenlik zaafiyeti nereden kaynaklanıyor ? Esasında bu zaafiyete sebep olan en temel unsur “çalışacak kod ile program verisini ayırmamak”tan kaynaklanıyor. Yani daha basit ifadeyle programın kontrolünü sağlayan yapıları ve program verisini aynı yerde tutmaktan kaynaklanıyor diyebiliriz. Çünkü veri kullanıcı tarafından oluşturulabilen ve düzenlenen bir unsur. Kontrol ve veriyi aynı yerde gördüyseniz orada çok büyük olasılıkla bir güvenlik açığı mevcuttur. Çünkü kullanıcı zararlı bir input girerek izni olmayan kontrol bölgelerine erişebilir ki bu da bizim istemediğimiz bir durum.

Peki esas konuya dönecek olursak, bu bahsettiğimiz kontrolü ve veriyi ayırma prensibi nerede ihlal edilmiş ki bu tarz saldırılara biz maruz kalabiliyoruz. Aslında C programlama dilinin stack alanını kullanış biçiminde biz bunu çok bariz bir şekilde görüyoruz.

Bir process’in stack’inin genel yapısı.

Burada görüldüğü üzere kodun text segmenti ve stack/heap/data segmenti aynı ortamda bulunuyor. Biraz daha detaylı incelendiğinde stack segmentin büyüme yönünün tehlikeli olabileceği kolaylıkla anlaşılabilir. Yani ben stack’i öyle bir doldurabilirim ki kod çalıştırılan segment olan text segmenti kafama göre oluşturup istediğim komutların çalışmasını sağlayabilirim.

İşte zafiyet burada başlıyor. Stack alanı, saldırganlar tarafından o kadar akıllıca bir şekilde dolduruluyor ve hatta taşırılıyor (overflow) ki kodun kontrol kısımlarına keyfi değerler yazılabiliyor. İşte bu tip saldırılara Stack Smashing ve Buffer Overflow gibi isimler veriliyor.

Saldırganlar buffer yani tampon bölgelerine öyle input değerleri yerleştiriyorlar ki sistem çalışması bozulmadan (crash olmadan) istedikleri kod parçasının text segmentte gözükmesini sağlıyorlar.

Basit örneklerle buffer overlfow’u kafamızda daha iyi canlandırabiliriz. Örneğin aşağıdak C kod parçacığına bakalım;

char benimGuzelBufferim[16];
strcpy(benimGuzelBufferim, "onaltikarakterdenuzunbirstring");

16 elemanlı bir karakter dizisi oluşturduk ve ona 16 karakterden fazla eleman içeren başka bir değer atadık. strcpy fonksiyonu kendi içerisinde boyut kontrolü yapmadığı için aslında bir buffer overflow zafiyetine de sebebiyet vermiş oldu. Gerçek hayat örneğine daha da yaklaştıracak olursak;

char benimGuzelBufferim[16];
strcpy(benimGuzelBufferim,argv[1]);

İşte tam bu kod örneğinde gerçek bir buffer overflow zafiyeti açıkça görülüyor, kullanıcıdan alından komut satırı argümanı doğrudan sabit uzunluklu bir buffer’a yazılıyor. Boyut kontrolü de yapılmadığı için kullanıcı rahatlıkla buradaki zafiyetten faydalanıp kontrolü ele geçirme saldırısı yapabilir.

Peki kod yazan ve uygulama geliştiren kişiler olarak, buffer overflow zaafiyetlerinden korunabilmek için yapılacak temel şeyler neler diye soracak olursanız; güvenli kod geliştirme prensiplerinden olan “always sanitize user input” yani kullanıcıdan gelen veriyi her koşulda her zaman temizleyin. Yani bir değişkene maksimum 16 karakter geliyorsa gelebilecek değeri 16 karakterle sınırlamalısınız. C programlama diline özel olarak aşağıdaki fonksiyonlar tasarımları gereği boyut kontrolü yapmazlar ve dolayısıyla buffer overflow zaafiyetine sebebiyet verebilirler;

  • strcat(),
  • strcpy(),
  • sprintf(),
  • vsprintf(),
  • bcopy(),
  • gets(),
  • scanf()

Kodun yazımından ziyade, kodun çalıştığı platform da buffer overflow zaafiyetlerine dirençli hale getirilebilir. Platformu daha güvenli hale getirmek için yapılan ek güncellemelere örnekler verecek olursak

NX(Non-Executable) Biti

İşlemcilerde stack bölgesi veri tutmak için vardır, dolayısıyla stackte bulunan veriler çalıştırılamamalıdır. İntel’in işlemcilerinde ortaya çıkan bu iyileştirme sayesinde kod çalıştırılmasına izin verilmeyen bölgeler bu bitle işaretlenebiliyor ve dolayısıyla stack alanı güvenli bir bölge haline getirilebiliyor.

Stack Canary

Kömür madenlerinde oksijen kaynaklarının yetersiz olması durumunda insanların önceden fark edebilmeleri için oksijen değişimine hassas olan kanaryalar madenin çeşitli yerlerine koyulurmuş. Kanaryalar oksijen değişiminden etkilenip zarar görürse o bölgede oksijenin yetersiz olduğu kanısına varılırmış. Bu yaklaşımı temel alan bir yöntemdir stack canary. Stack’in belli bölgelerine küçük tam sayı değerleri yerleştirilerek program başlatılır. Saldırgan buffer’ı zararlı bir veriyle doldurduğunda canary’leri de değiştireceğinden girilen değerin zararlı olup olmadığı anlaşılabilir.

ASLR (Address Space Layout Randomization)

Özellikle return oriented programming (ROP) ve return2libc saldırılarına çok güçlü bir çözüm sağlamaktadır. Yani saldırgan zararlı bir girdiyle birlikte libc kütüphanesinden fonksiyon çağrısı yapabilir (genellikle exec ailesinden çağırır). Bunu yapabilmesi için işletim sistemi açıldığında libc kütüphanesinin hangi hafıza adresine yerleştiğini bilmesi yeterlidir. Dolayısıyla libc’yi işletim sistemi her açıldığında aynı hafıza bölümüne yazmak güvenlik zafiyeti doğurur. ASLR teknolojisi de libc gibi kütüphaneler, daha işletim sistemi açılırken rastgele adres uzaylarına yerleştirerek tahmin edilebilir adreslerde olmasını engellemektedir.

Aklınıza “buffer overflow zaafiyeti içeren bir uygulama nasıl geliştirilebilir ki ” diye bir soru takılırsa aşağıdaki yazımı inceleyebilirsiniz.

Bu yazı bana yetmez ben işin membasından öğrenmek istiyorum derseniz de Stanford Üniversitesi’nde verilen CS155 kodlu dersin kaynaklarını incelemeniz oldukça faydalı olacaktır.

--

--

Burak Tahtacı
Burak Tahtacı

Written by Burak Tahtacı

TA1KNT / Computer Engineer / RC Hobbyist / Data Science and Machine Learning Lover also interested in Information Security

No responses yet