Yığın arabellek taşması - Stack buffer overflow

Yazılımda, bir program , genellikle sabit uzunluklu bir arabellek olan amaçlanan veri yapısının dışında , programın çağrı yığınındaki bir bellek adresine yazdığında yığın arabellek taşması veya yığın arabellek taşması meydana gelir . Yığın arabelleği taşması hataları, bir program yığında bulunan arabelleğe, bu arabellek için ayrılandan daha fazla veri yazdığında ortaya çıkar. Bu hemen hemen her zaman yığındaki bitişik verilerin bozulmasına neden olur ve taşmanın yanlışlıkla tetiklendiği durumlarda, genellikle programın çökmesine veya yanlış çalışmasına neden olur. Yığın arabellek taşması, arabellek taşması (veya arabellek taşması) olarak bilinen daha genel programlama arızasının bir türüdür . Yığındaki bir arabelleği aşırı doldurmak, yığındaki tüm etkin işlev çağrıları için dönüş adreslerini içerdiğinden, yığındaki bir arabelleği aşırı doldurmaktan ziyade program yürütmeyi raydan çıkarma olasılığı daha yüksektir.

Bir yığın arabellek taşmasına, yığın parçalama olarak bilinen bir saldırının parçası olarak kasıtlı olarak neden olunabilir . Etkilenen program özel ayrıcalıklarla çalışıyorsa veya güvenilmeyen ağ ana bilgisayarlarından (örneğin bir web sunucusu ) veri kabul ediyorsa, hata potansiyel bir güvenlik açığıdır. Yığın arabelleği, güvenilmeyen bir kullanıcıdan sağlanan verilerle doldurulursa, bu kullanıcı yığını, çalışan programa yürütülebilir kod enjekte edecek ve işlemin kontrolünü ele geçirecek şekilde bozabilir. Bu, saldırganların bir bilgisayara yetkisiz erişim elde etmesi için en eski ve daha güvenilir yöntemlerden biridir .

Yığın arabellek taşmalarından yararlanma

Yığın tabanlı bir arabellek taşmasından yararlanmanın kurallı yöntemi, saldırgan tarafından kontrol edilen verilere (genellikle yığının kendisinde) bir işaretçi ile işlev dönüş adresinin üzerine yazmaktır. Bu, strcpy()aşağıdaki örnekte gösterilmiştir:

#include <string.h>

void foo(char *bar)
{
   char c[12];

   strcpy(c, bar);  // no bounds checking
}

int main(int argc, char **argv)
{
   foo(argv[1]);
   return 0;
}

Bu kod, komut satırından bir argüman alır ve onu yerel bir yığın değişkenine kopyalar c. Bu, 12 karakterden küçük komut satırı argümanları için iyi çalışır (aşağıdaki Şekil B'de görebileceğiniz gibi). 11 karakterden uzun bağımsız değişkenler yığının bozulmasına neden olur. (Güvenli olan maksimum karakter sayısı, buradaki arabelleğin boyutundan bir eksiktir, çünkü C programlama dilinde, dizeler boş bir bayt karakteriyle sonlandırılır. Bu nedenle, on iki karakterlik bir girdinin saklanması için on üç bayt gerekir, girdi ardından Nöbetçi sıfır bayt tarafından. Sıfır bayt daha sonra arabelleğin bir bayt ötesindeki bir bellek konumunun üzerine yazar.)

Program foo()çeşitli girdilerle yığınlanır :

A. - Veriler kopyalanmadan önce.
B. - "merhaba" ilk komut satırı argümanıdır.
C. - "AAAAAAAAAAAAAAAAAAAA\x08\x35\xC0\x80" ilk komut satırı argümanıdır.

Yukarıdaki şekil C'de, komut satırında 11 bayttan büyük bir argüman sağlandığında foo()yerel yığın verilerinin, kaydedilen çerçeve işaretçisinin ve en önemlisi dönüş adresinin üzerine yazdığına dikkat edin . Döndüğünde foo(), geri dönüş adresini yığından çıkarır ve o adrese atlar (yani, o adresten talimatları yürütmeye başlar). Böylece, saldırgan, char c[12]artık saldırgan tarafından sağlanan verileri içeren yığın arabelleğine bir işaretçi ile dönüş adresinin üzerine yazmıştır. Gerçek bir yığın arabellek taşmasında, "A" dizisinden yararlanın, bunun yerine platforma ve istenen işleve uygun kabuk kodu olacaktır . Bu programın özel ayrıcalıkları varsa (örneğin, süper kullanıcı olarak çalışacak şekilde ayarlanmış SUID biti ), saldırgan, etkilenen makinede süper kullanıcı ayrıcalıkları elde etmek için bu güvenlik açığını kullanabilir.

Saldırgan, bazı hatalardan yararlanmak için dahili değişken değerlerini de değiştirebilir. Bu örnekle:

#include <string.h>
#include <stdio.h>

void foo(char *bar)
{
   float My_Float = 10.5; // Addr = 0x0023FF4C
   char  c[28];           // Addr = 0x0023FF30

   // Will print 10.500000
   printf("My Float value = %f\n", My_Float);

    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       Memory map:
       @ : c allocated memory
       # : My_Float allocated memory

           *c                      *My_Float
       0x0023FF30                  0x0023FF4C
           |                           |
           @@@@@@@@@@@@@@@@@@@@@@@@@@@@#####
      foo("my string is too long !!!!! XXXXX");

   memcpy will put 0x1010C042 (little endian) in My_Float value.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

   memcpy(c, bar, strlen(bar));  // no bounds checking...

   // Will print 96.031372
   printf("My Float value = %f\n", My_Float);
}

int main(int argc, char **argv)
{
   foo("my string is too long !!!!! \x10\x10\xc0\x42");
   return 0;
}

Platformla ilgili farklılıklar

Bir dizi platform, çağrı yığınının uygulanmasında, yığın arabellek taşması istismarının çalışma şeklini etkileyebilecek küçük farklılıklara sahiptir. Bazı makine mimarileri, çağrı yığınının en üst düzey dönüş adresini bir kayıt defterinde saklar. Bu, üzerine yazılan herhangi bir geri dönüş adresinin, çağrı yığınının daha sonra çözülmesine kadar kullanılmayacağı anlamına gelir. İstismar tekniklerinin seçimini etkileyebilecek makineye özgü bir ayrıntının başka bir örneği, çoğu RISC tarzı makine mimarisinin belleğe hizalanmamış erişime izin vermeyeceği gerçeğidir . Makine işlem kodları için sabit bir uzunlukla birleştiğinde, bu makine sınırlaması, ESP tekniğine atlamayı neredeyse imkansız hale getirebilir (bir istisna, programın aslında yığın kaydına açıkça atlamak için olası olmayan kodu içermesidir).

Büyüyen yığınlar

Yığın arabellek taşmaları konusunda, sıklıkla tartışılan ancak nadiren görülen bir mimari, yığının ters yönde büyüdüğü bir mimaridir. Mimarideki bu değişiklik, aynı yığın çerçevesi içinde meydana gelen bir yığın arabelleğinin herhangi bir taşması dönüş işaretçisinin üzerine yazamayacağından, yığın arabellek taşması sorununa bir çözüm olarak sıklıkla önerilmektedir. Bu iddia edilen korumanın daha fazla araştırılması, en iyi ihtimalle naif bir çözüm olduğunu bulur. Önceki bir yığın çerçevesinden bir arabellekte meydana gelen herhangi bir taşma, yine de bir geri dönüş işaretçisinin üzerine yazacak ve hatanın kötü niyetli olarak kullanılmasına izin verecektir. Örneğin, yukarıdaki örnekte foo, taşma aslında için yığın çerçevesi içinde gerçekleştiğinden için dönüş işaretçisinin üzerine yazılmaz memcpy. Ancak, çağrı sırasında taşan arabellek memcpyönceki bir yığın çerçevesinde bulunduğundan, dönüş işaretçisi memcpyarabellekten sayısal olarak daha yüksek bir bellek adresine sahip olacaktır. Bu foo, üzerine yazılacak dönüş işaretçisi yerine, dönüş işaretçisinin üzerine yazılacağı anlamına gelir memcpy. Bu, en fazla, yığının ters yönde büyütülmesinin yığın arabellek taşmalarının nasıl kötüye kullanılabileceğinin bazı ayrıntılarını değiştireceği, ancak yararlanılabilir hataların sayısını önemli ölçüde azaltmayacağı anlamına gelir.

Koruma şemaları

Yıllar boyunca, kötü niyetli yığın arabellek taşması istismarını engellemek için bir dizi kontrol akışı bütünlük şeması geliştirilmiştir. Bunlar genellikle üç kategoriye ayrılabilir:

  • Bir yığın arabellek taşması olduğunu tespit edin ve böylece talimat işaretçisinin kötü amaçlı koda yeniden yönlendirilmesini önleyin.
  • Yığın arabellek taşmasını doğrudan algılamadan yığından kötü amaçlı kodun yürütülmesini önleyin.
  • Yürütülebilir kod bulmanın güvenilmez hale gelmesi için bellek alanını rastgele düzenleyin.

Kanarya yığını

Kömür madenindeki bir kanaryaya benzetmeleriyle adlandırılan yığın kanaryaları, kötü amaçlı kodun yürütülmesinden önce bir yığın arabellek taşmasını algılamak için kullanılır. Bu yöntem, program başlangıcında değeri rastgele seçilen küçük bir tamsayıyı yığın dönüş işaretçisinden hemen önce belleğe yerleştirerek çalışır. Çoğu arabellek taşması, daha düşük bellek adreslerinden daha yüksek bellek adreslerine bellek üzerine yazar, bu nedenle dönüş işaretçisinin üzerine yazmak (ve böylece işlemin kontrolünü ele geçirmek) için kanarya değerinin de üzerine yazılması gerekir. Bu değer, bir rutin yığındaki dönüş işaretçisini kullanmadan önce değişmediğinden emin olmak için kontrol edilir. Bu teknik, bir yığın arabellek taşmasından yararlanmanın zorluğunu büyük ölçüde artırabilir, çünkü saldırganı yığındaki diğer önemli değişkenleri bozmak gibi geleneksel olmayan bazı yollarla talimat işaretçisinin denetimini ele geçirmeye zorlar.

Yürütülebilir olmayan yığın

Yığın arabellek taşması istismarını önlemeye yönelik başka bir yaklaşım, yığından yürütmeye izin vermeyen yığın bellek bölgesinde bir bellek ilkesi uygulamaktır ( W^X , "XOR Yürüt Yaz"). Bu, bir saldırganın yığından kabuk kodunu yürütmek için, yürütme korumasını bellekten devre dışı bırakmanın bir yolunu bulması veya kabuk kodu yükünü korumalı olmayan bir bellek bölgesine koymanın bir yolunu bulması gerektiği anlamına gelir. Çoğu masaüstü işlemcisinde yürütme yok bayrağı için donanım desteği bulunduğundan, bu yöntem artık daha popüler hale geliyor.

Bu yöntem, yığın arabellek taşması sömürüsüne yönelik kurallı yaklaşımı kesinlikle başarısız kılarken, sorunsuz değildir. İlk olarak, kabuk kodunu yığın gibi korumasız bellek bölgelerinde saklamanın yollarını bulmak yaygındır ve bu nedenle sömürü yolunda çok az değişiklik yapılması gerekir.

Bu böyle olmasa bile, başka yollar da var. En zararlı olanı, kabuk kodu oluşturma için sözde libc'ye dönüş yöntemidir. Bu saldırıda, kötü niyetli yük yığını kabuk koduyla değil, uygun bir çağrı yığınıyla yükler, böylece yürütme, genellikle bellek yürütme korumalarını devre dışı bırakma ve kabuk kodunun normal şekilde çalışmasına izin verme etkisiyle, yürütme bir standart kitaplık çağrıları zincirine yönlendirilir. Bu işe yarar, çünkü yürütme hiçbir zaman yığının kendisine vektör etmez.

Libc'ye dönüşün bir varyantı , her biri mevcut program kodu veya sistem kitaplıkları içinde küçük bir özenle seçilmiş makine talimatları dizisini yürüten bir dizi dönüş adresi ayarlayan dönüş yönelimli programlamadır (ROP). dönüşle biter. Bu sözde gadget'ların her biri, geri dönmeden önce bazı basit kayıt işlemleri veya benzer işlemleri gerçekleştirir ve bunları bir araya getirmek, saldırganın amaçlarına ulaşır. Hatta bir geri dönüş talimatı gibi davranan talimatlar veya talimat gruplarını kullanarak "geri dönüşü olmayan" geri dönüş odaklı programlamayı kullanmak bile mümkündür.

rastgeleleştirme

Kodu verilerden ayırmak yerine, başka bir azaltma tekniği, yürütülmekte olan programın bellek alanına rasgeleleştirme eklemektir. Saldırganın kullanılabilecek yürütülebilir kodun nerede bulunduğunu belirlemesi gerektiğinden, ya yürütülebilir bir veri yükü sağlanır (çalıştırılabilir bir yığınla birlikte) ya da ret2libc veya dönüş yönelimli programlamada (ROP) olduğu gibi kodun yeniden kullanımı kullanılarak bir yük oluşturulur. Bellek düzenini rastgele ayarlamak, bir kavram olarak, saldırganın herhangi bir kodun nerede olduğunu bilmesini engelleyecektir. Ancak, uygulamalar tipik olarak her şeyi rastgele yapmayacaktır; genellikle yürütülebilir dosyanın kendisi sabit bir adrese yüklenir ve bu nedenle ASLR (adres alanı düzeni rastgeleleştirmesi) yürütülemez bir yığınla birleştirildiğinde bile saldırgan bu sabit bellek bölgesini kullanabilir. Bu nedenle, tüm programlar PIE (konumdan bağımsız yürütülebilir dosyalar) ile derlenmelidir, öyle ki bu bellek bölgesi bile rasgele hale getirilmelidir. Rastgeleleştirmenin entropisi, uygulamadan uygulamaya farklıdır ve yeterince düşük bir entropi, rastgeleleştirilmiş bellek alanını kaba zorlama açısından kendi başına bir sorun olabilir.

Önemli örnekler

Ayrıca bakınız

Referanslar