Heap Overflow Atak Nedir? Nasıl Yapılır?

 

Diyelim ki, yapılması gereken işleriniz var ve bu işlerin elinizde bir listesi var.  Bir bilgisayar gibi düşünürsek, listeye bakarak sıradaki yapılması gereken işi yaparız ve bir sonraki iş için tekrar listeye bakıp o işi yaparız.  Bilgisayar, programları bu şekilde çalıştırır. İşlemci bir programı, “program counter” ismi verilen kaydediciye bakarak işler. Bu kaydediciye program komutlarının adresleri sıra sıra yerleştirilir.  Program,  yazılan komut adresleri üzerinden işlendiği için ramdeki bu adresler sabote edilerek kötü amaçlı olarak yönlendirilebilir. Exploit dediğimiz programlar, hedef programların açıklarından faydalanıp program hafızasını bu şekilde sabote ederler.

Daha önceki StackOverflow Atak isimli yazımda bunlardan bahsetmiştim ama tekrar hatırlamakta fayda var.

Bir program çalıştığında program için ramden belli kalıplara sahip alanlar tahsil edilir. Bu alanlar, linux işletim sistemlerinde şu şekildedir;

Program çalışırken programa ram den  heap ve stack adı verdiğimiz iki önemli alan tahsis edilir. Exploitler bu iki bölümü sabote etmeye yoğunlaşırlar.  Stack bölümünün nasıl sabote edildiği hakkında bahsetmiştim.  Heap bölümü, programda dinamik olarak tahsis edilebilinen bir alandır. Malloc(), alloc() gibi C fonksiyonlarıyla tahsis edilir ve free() fonksiyonuyla da bu bölümler serbest bırakılarak tekrar kullanıma açılabilir.  Örnek olarak şu programa göz atalım;

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <inttypes.h>

struct Object {

        char    *name;
        void    (*operation)();
};


void secret()
{
        printf("We are in secret @ %d\n", 12);
}



int main(int argc, char **argv)
{
        struct Object *o1, *o2;
        const char *str1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x20\x80\x55\x55\x55\x55\x00\x00";
        const char *str2 ="\x8a\x51\x55\x55\x55\x55\x00\x00";

        o1 = malloc(sizeof(struct Object));
        o1 -> name = malloc(8);
        o2 = malloc(sizeof(struct Object));
        o2 -> name = malloc(8);

        o1->operation = 0x10;
        o2->operation = 0x10;

        strcpy(o1->name, str1);
        strcpy(o2->name, str2);

        printf("main\n!\n");
}





 

Programda object ismindeki bir struct içerisinde biri “char pointer” ve bir “function pointer” olmak üzere iki değişken tanımlanmış. Bu struct, heap bölümünde dinamik olarak oluşturulup içerisine str değerleri yerleştirilmiş. Fakat dikkat ettiğiniz üzere strcpy fonksiyonları herhangi bir uzunluk belirtmediğinden dolayı programa str değerleri ile istenilen uzunlukta veri girilebilir durumda. Bu da bize heap bölümünü overflow edebilmek için bir olanak sağlıyor.

Yukarıdaki programda malloc fonksiyonuyla ayrılan heap bölümündeki alanlar temel olarak DLMalloc algoritmasına göre yapılanıyor. malloc fonsiyonu heap bölümünden istenilen kadar alan ayırıp, ayrılan alanın adresini döndüren bir fonsiyondur.

o1 = malloc(sizeof(struct object)); komutuyla yukarıdaki tablodaki gibi, object struct’ı için 32 byte’lık alan ayrılır.  Bunun sebebi, 64 bitlik bir sistemde malloc fonksiyonunun heap’i 32 bytelık bloklar halinde tahsis etmesidir. Bu ayrılan  alanın önünde bu alanın kaç byte olduğu bilgisi yukarıdaki tablodaki “size” şeklinde tanımlanmış bölüme yerleştirilir.  Eğer bir önceki alan aynı değişkene ait değilse yani kullanımdaysa bu alanın son biti “1” set edilir. Yani 64 bytelık bir sistemde burası 0x21 olacaktır. Çünkü 0x20, 32 byte demek, 0x01 ise bir önceki alanın başka değişken tarafından kullanıldığını ifade etmektedir.. 

Struct için bir alan ayrıldıktan sonra bu struct içerisinde, name isimli adreste tutulacak olan  bir char dizi için de 8 byte’lık bir alan, “o1->name = malloc(8);” komutuyla ayrılmış.  Bu alanı da yukarıdaki tabloda 2. satır olarak düşünebilirsiniz.

Yukarıdaki tabloda her hücrenin bir adresi vardır. char * name değişkeni 2. satırdaki ilk “free 8 byte” lık alanın adresini tutmaktadır.   Sonuç olarak yukarıdaki programda heap bölümünde aşağıdaki gibi alanlar ayrılmış olur.

Teorik olarak bu alanı sabote edebilmenin en güzel yolu ikinci struct için ayrılan alandaki name isimli adresi overflow edip gerçek adresi yerine istenilen ve programın başka bir fonksiyona atlamasına yatarayacak bir adres yazmaktır.. Bu sayede programı exploit etmiş oluruz.

Yani overflow edilmiş heap şu eşkilde olmuş olur;

 

Kırmızı işaretli hafıza bölgerelerine komut satırından veri yazabiliriz.  Normal şartlarda kullanıcının bu alanlardan sadece “free 8 byte” ismindeki alanlara veri girişi yapabilmesi gerekirdi. Ancak veri girişine ve bu verilerin hafızaya aktarılmasına herhangi bir kısıtlama getirilmemesinden dolayı, kullanıcı üzerine yazmaması gereken adreslere de veri yazabildi.  Bu sayede kullanıcı, programı kötü amaçlı kullanılabilecek komut adreslerine yönlendirebildi.

Aslında “o2->name” değişkeninin adresini tutan bu alana kötü amaçlı bir adres yazarak bir dahaki strcpy fonksiyonunda istediğimiz bir alana istediğimiz değeri yazma şansı yakalamış olduk. Eğer buraya “global offset table” adreslerini ezersek, program printf yada herhangi bir standart fonksiyonunu çağırarak aslında bizim istediğimiz adresteki fonksiyonu çağırır.

İşin pratik kısmını kendiniz denemek isterseniz  bu programı herhangi bir C debugger(bunlardan en yaygın kullanılanı gdb dir) aracı kullanarak çalıştırabilir ve veri alanlarını kendiniz görebilirsiniz. Şimdilik İşin pratik kısmını buraya yazma gereği duymuyorum.  Çünkü gdb gibi debugger programları kullanmak ayrı bir yazının konusu olacak inşallah.

Burada yazdıklarımı , şu adresten gdb ile nasıl uygulandığını görebilirsiniz.  Bu yazı içeriği belirtilen adresten alıntılanmıştır.

 

 

Yusuf

Yusuf

Bir Mühendis.

Önerilen makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Translate »