BUFFER OVERFLOW
(ARABELLEK TAŞMASI)
NEDİR?

ARABELLEK (BUFFER) NEDİR?

Arabellek (buffer), farklı hızlarda veya farklı öncelik kümeleriyle çalışan donanım aygıtları veya program işlemleri tarafından paylaşılan bir veri alanıdır. Her aygıtın veya işlemin başka bir aygıt ya da işlem tarafından müdahale edilmeden veya engellenmeden çalışmasına izin verir.

Ayrıca, bir arabellek işlenmekte olan verileri tutmak için kullanılan bir program içinde ayrılmış bir bellek kesimini oluşturur. Gelen ve giden verileri tutmak için her programda arabellek kullanımları mevcuttur. Örneğin, bir video uygulamasında, program anlık gecikmeleri telafi etmek ve önceden sağlanan video verilerini depolamak için arabellekler kullanır.

Bir bilgisayarda, her uygulama kendi arabelleğini genel bellek havuzundan ayırabilir veya ayırmış olduğu ara belleği bu havuza tekrardan dahil edebilir. Örneğin, bir yazıcıda ve diğer çevre birimlerinde, sabit arabellekler gelen giden veriler için geçici depolamayı varsayılan olarak sağlar.

Bir arabelleğin etkin olabilmesi için arabelleğin boyutu ve verileri arabelleğin içine ve dışına taşıma algoritmalarının arabellek tasarımcısı tarafından dikkate alınması gerekir.

Aşağıdaki örnek kod satırları, arabelleklerin (programlama dili) C’de nasıl tanımlandığını gösterir. “/*” ve “*/” işaretleri açıklamaları (yorumları) temsil ederken, char ve long ise C dilinin öğeleridir. GELENbuff, veri okumak için ayrılmış 1000 baytlık bir arabellek kullanır:

İşletim sistemi seviyesinde bir işlem aşağıdaki şekilde çalışır:

https://miro.medium.com/max/1280/0*MJe3qd_LxrMEyWjL.jpeg

Stack: Yerel değişkenler söz konusu olunca devreye girer. Stack içerisinde yer alan boş kısımlar değişkenler belirlendikçe bu değişkenler için atanır ve stack işlevlerinin geri dönüş değerlerini saklamak için de stack devrede olur.

Heap: Dinamik bellek kontrolü için kullanılır, genellikle spesifik çağrılar yapılarak devreye sokulur.

Data: Sistem çapında ve statik değişkenler bu kısımda yer alır ve ana işlevler devreye girmeden önce tüm değerleri atanmış ve ayarlanmış olur.

Text: Derlenmiş kodun tutulduğu yerdir.

Bir işlevi veya fonksiyonu çağırdıktan sonra çeşitli işlemler gerçekleşir, program çalışır ve veri akışı meydana gelir, daha sonra bu işlemler bellekte bulunduğu yer ile sürekli irtibat halinde olarak çeşitli değerler döndürür. Programlar çalışıp belirlenen parametrelere veri girişi yapıldıktan sonra yani işlevler görevlerini tamamladığında yığının (stack) yukarısına doğru itilir, böylece yığın (stack) üzerindeki parametreler ile birlikte işlevin başka bir yere ya da adrese atlamasıyla sürekli bu döngü devam eder.

ARABELLEK TAŞMASI (BUFFER OVERFLOW) NEDİR?

Arabellek taşması en sade haliyle, bir programın veya işlemin bellekte kendisine ayrılan yere veri yazarken, bu belirlenmiş sınırları aşması olarak nitelendirilebilir. Sonuç olarak saldırgan bu taşan yerde, yazmış olduğu kötü niyetli kodu çalıştırarak sistemi ele geçirir.

Bir arabellek, verileri belirlenen kısmın sol veya sağ kısmından veri taşırırsa, belirlenen sınırı geçmiş olur, yani arabellek taşması meydana gelir. Bu şekilde veriler, arabelleğe başvuran program değişkenine ait olmayan belleğin bir bölümüne yazılır.

Aşağıdaki örnekte, 10 baytlık bir dizin bildiriyoruz. Dizin 0’dan dizin 9’a bu 10 bayt arabelleğe başvurmak için kullanılmaktadır. Ancak, bir sonraki satırda, ‘a’ değerini depolamak için 10 dizinini kullandığımız için, verilerin arabelleğin sağ sınırının ötesine yazılması nedeniyle arabellek taşmasının gerçekleştiği yer olur.

Arabellek taşmasını istismar etme, saldırgana bir işlemi kontrol etmesine veya sistemi çökertmesine ya da iç değişkenler üzerinde oynama yapmasına olanak tanır.

Arabellek taşması programlama hatası sonucu yanlışlıkla ortaya çıkabilir veya kötü amaçlı bir aktör tarafından meydana çıkarılabilir. Bir saldırgan, bir programa – rastgele kod yürütme (arbitrary code execution) olarak adlandırılan – özenle hazırlanmış bir girdi gönderebilir. Program bu girdiyi büyük olmayan bir arabellekte depolamaya çalışır. Arta kalan veri daha sonra sağ kısımda bulunan bitişik belleğe yazılırsa, arabellek taşması meydana gelir ve saldırgana hareket edebileceği bir alan oluşturmuş olur.

Arabellekteki orijinal veriler, işlemin bir sonraki aşamasına geçiş için gerekli olan bilgileri içerir. (Örn. işaretçi, dönüş işaretçisi vb.) Ancak saldırgan, bu veriler üzerinde oynamalar yaparak seçtiği bir adrese işaret edecek yeni değerler ayarlayabilir. Böylece, saldırgan genellikle yeni değerleri istismar yükünün (exploit payload) konumlandırıldığı bir konuma ayarlar. Bu değişiklik işlemin yürütme yolunu ve işaretçeilerini değiştirerek, denetimi saldırganın kötü amaçlı koduna yönlendirir.

Örneğin, bir programın kullanıcıların adlarını girmelerini beklediğini varsayalım. Adını girmek yerine, saldırgan yığın boyutunu aşan yürütülebilir bir komut girer. Komut genellikle kısadır. Örneğin, bir Linux ortamında, komut genellikle sisteme Linux çevrelerinde kök kabuk (root shell) olarak bilinen bir komut istemi penceresi açmasını söyleyen EXEC(“sh”) şeklindedir.

Ancak, arabelleğin yürütülebilir bir komutla taşması, komutun yürütüleceği anlamına gelmez. Saldırganın kötü amaçlı komutu işaret eden bir dönüş adresi belirtmesi gerekir. Yığın taştığı için program kısmen çöker. Daha sonra dönüş adresine giderek bunu kurtarmaya çalışır, ancak dönüş adresi saldırganın belirttiği komutu gösterecek şekilde değiştirildiği için, kötü amaçlı komutun çalışacağı adresi bilmesi gerekir.

Gerçek adrese ihtiyaç duymamak için, kötü amaçlı komut genellikle her iki tarafa da bir tür işaretçi olan NOP – veya işlem yapma! – talimatıyla doldurulur. Her iki tarafı da bu şekilde doldurma (padding), tam bellek aralığı bilinmediğinde kullanılan bir tekniktir. Saldırganın belirttiği adres bu doldurulmuş alanların içinde herhangi bir yere düşerse, kötü amaçlı komut çalıştırılır.

C ve C++ gibi programlama dilleri, belleklerinin herhangi bir bölümündeki verilere erişmeye veya verilerin üzerine yazmaya karşı hiçbir korumaya sahip değildir. Sonuç olarak, arabellek taş(ır)ma saldırılarına karşı savunmasızdırlar. Saldırganlar, ortak programlama yapılarıyla doğrudan bellek manipülasyonu gerçekleştirebilir.

C#, Java ve Perl gibi modern programlama dilleri, kodlama hatalarının arabellek taşması güvenlik açıkları oluşturma olasılığını azaltır. Bununla birlikte, arabellek taşmaları, program derleyicisindeki kusurlar, çalışma zamanı kitaplıkları veya dilin özellikleri aracılığıyla doğrudan bellek manipülasyonuna izin verilen herhangi bir programlama ortamında gerçekleşebilir.

EN YAYGIN ARABELLEK TAŞMASI ÇEŞİTLERİ NELERDİR?

Arabellek taşması güvenlik açıklarını istismar etme teknikleri işletim sistemine ve programlama diline göre değişir. Ancak, amaç her zaman bir bilgisayarın belleğini manipüle ederek program yürütülmesinin kontrolünü ele geçirerek bilgisayarın belleğinde değişiklikler yapmaktır.

Arabellek taşmaları, arabelleğin işlem belleğindeki konumuna göre kategorilere ayrılır. Bunlar çoğunlukla yığın tabanlı (stack-based) taşmalar veya alt yığın tabanlı (heap-based) taşmalardır. Her iki kategori de bir cihazın rastgele erişimli belleğinde (random access memory (RAM)) meydana gelir.

Yığın tabanlı (Stack-based) arabellek taşması

Yığın, verileri son giren ilk çıkan (last-in – first-out structure) yapıda tutar. İşlev parametreleri, işlev yerel değişkenleri, çerçeve ve yönerge işaretçileri gibi yönetim bilgileri de dahil olmak üzere işlev çağrıları ile ilişkili verileri düzenlemek için kullanılan bellekte sürekli bir alandır. Normalde, hedeflenen program kullanıcı adı veya parola gibi kullanıcı girişi gerektirene kadar yığın boştur. Bu noktada, program yığına bir dönüş belleği adresi yazar ve ardından kullanıcının girişi üstüne yerleştirilir. Yığın işlendiğinde, kullanıcının girişi program tarafından belirtilen dönüş adresine gönderilir.

Ancak, bir yığın sınırlı bir büyüklüğe sahiptir. Kodu geliştiren programcı, yığın için belirli miktarda alan ayırmalıdır. Kullanıcının girişi yığın içinde kendisine ayrılan alan miktarından daha uzunsa; program, girdinin ayrılan bölüme sığacağını doğrulamazsa, yığın taşar. Bu kendi başına büyük bir sorun değildir, ancak kötü amaçlı girdilerle birleştirildiğinde büyük bir güvenlik açığı haline gelmektedir.

Alt (Dinamik) yığın tabanlı (Heap-based) arabellek taşması

Alt yığın, dinamik belleği yönetmek için kullanılan bir bellek yapısıdır. Programcılar genellikle yığın derleme anında boyutu bilinmeyen, gereken bellek miktarının alt yığına sığmayacak kadar büyük olduğu veya belleğin işlev çağrıları arasında kullanılması amaçlanan belleği ayırmak için kullanır. Alt yığın tabanlı saldırılar, bir program veya işlem için ayrılmış bellek alanını taşırır. Alt yığın tabanlı güvenlik açıklarını istismar etmek oldukça zordur, bu nedenle yığın saldırılarından daha nadirdir.

ARABELLEK TAŞMASI ÖRNEK PROGRAM

Aşağıda yazmış olduğumuz basit program senaryosu, kullanıcıdan bir şifre girmesini istemekte ve girilen şifrenin doğru olması durumunda kullanıcıya yetkili erişim izni vermektedir.

Şimdi bu programı doğru şifre ile çalıştıralım:

Programa doğru şifre ile giriş yapıldığında beklenen sonuç elde edilmektedir. Fakat yazmış olduğumuz program içerisinde “gets()” fonksiyonu girilen verini sınırlarını denetlememektedir, sonuç olarak arabellek boyutundan daha yüksek veri girişi yapıldığı zaman arabellek taşması meydana gelecektir.

Şimdi de yazmış olduğumuz program bizden şifre talep ettiği zaman doğru şifre yerine rastgele ve normalden çok daha uzun bir veri girdiğimiz zaman ne oluyor ona bakalım:

Yukarıdaki örnekte, yanlış bir şifre girdikten sonra bile, program doğru şifreyi vermiş gibi çalışmaktadır.

Yukarıda almış olduğumuz sonucun mantığı; arabelleğin tutabileceğinden daha büyük boyutta bir değer ile veri girişi yapılmasının sonucu olarak arabellek taşması meydana gelmektedir. Böylelikle, taşan arabellek kendisinden sonra gelen tam sayının (integer) “pass” üzerine yazmaktadır, böylelikle yanlış bir şifre yazmamıza rağmen “pass” tam sayısı (integer) değeri sıfır olarak kabul edilmedi ve veri girişi yapan kişiye yetkili erişim izni verdi.

Arabellek taşmasına yol açabilecek birkaç ileri seviye farklı teknik (kod enjeksiyonu veya kod yürütme gibi.) mevcuttur, fakat bu ileri seviye tekniklere geçmeden önce arabelleğin temellerini ve yukarıda görmüş olduğumuz gibi taşmaların neden meydana geldiğini bilmek başlangıç için çok önemlidir.

SONUÇ

Makalenin birinci bölümünde arabellek (buffer) nedir, arabellek taşması (buffer overflow) nedir ve en yaygın arabellek taşma türleri nelerdir bunları öğrendik ve arabellek taşma zafiyeti içeren basit bir program yazdık.

Bahsettiğimiz üzere arabellek taşmasının temellerini, neden ve nasıl meydana geldiğini öğrenmek sonraki ileri seviye istismar aşamaları için yapı taşı görevi görecektir.

YAZAN: ABDULHAKİM ÖNER – SPARTA BİLİŞİM SIZMA TESTİ UZMANI

Başa dön