Burada Heap'in Borusu Öter!

Dün nereden çıktıysa bilmiyorum bi heap overflow'a girişesim geldi.  Protostar'ın heap challengeları ile başlayayım dedim. Unutursam diye çözümlerinide buraya yazıyorum.

Heap 0

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

    struct data {
      char name[64];
    };

    struct fp {
      int (*fp)();
    };

    void winner()
    {
      printf("level passed\n");
    }

    void nowinner()
    {
      printf("level has not been passed\n");
    }

    int main(int argc, char **argv)
    {
      struct data *d;
      struct fp *f;

      d = malloc(sizeof(struct data));
      f = malloc(sizeof(struct fp));
      f->fp = nowinner;

      printf("data is at %p, fp is at %p\n", d, f);

      strcpy(d->name, argv[1]);
      
      f->fp();

    }
İki adet struct var ve main içerisinde malloc ile bunlara heap'ten yer ayırıldığını görüyoruz. Bizden istenen açık, d struct'ının alanını taşırıp f struct'ının içerisindeki fp fonksiyon işaretçisini değiştirerek programı winner() fonksiyonuna yönlendirmek. Kolaylık olsun diye iki struct'ın adresinide yazdırmışlar sağolsunlar.

Normal bir argümanla programı çalıştırıp adresleri aldık. Daha sonra bu iki adresi birbirinden çıkararak offset değerini buluyoruz.

Offsetimiz 72 imiş. Şimdi ihtiyacımız olan son şey winner() fonksiyonunun adresi. Onu da objdump yardımıyla öğrendikten sonra exploitimizi yazabiliriz.

Adresimizi de  bulduğumuza göre artık exploit işlemine hazırız.

Böylelikle ilk levelimizi geçmiş bulunuyoruz.

Heap 1

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

      

    struct internet {
      int priority;
      char *name;
    };

    void winner()
    {
      printf("and we have a winner @ %d\n", time(NULL));
    }

    int main(int argc, char **argv)
    {
      struct internet *i1, *i2, *i3;

      i1 = malloc(sizeof(struct internet));
      i1->priority = 1;
      i1->name = malloc(8);

      i2 = malloc(sizeof(struct internet));
      i2->priority = 2;
      i2->name = malloc(8);

      strcpy(i1->name, argv[1]);
      strcpy(i2->name, argv[2]);

      printf("and that's a wrap folks!\n");
    }
Bu leveli ayrı bi sevdim neden bilmiyorum. Neyse, gördüğümüz üzere internet adlı bir struct'ımız bulunmakta, içerisinde ise name adlı bi char pointer ile priority adlı bir integer değişken var. Daha sonra malloc ile aynı tipten iki adet struct'a heap alanından yer ayırılmış ve strcpy() ile bu struct'ların içerisindeki char pointerların tuttukları adreslere bizim argümanlar aracılığıyla gönderdiğimiz stringler yazılmış. Levelin amacı her zamanki gibi program akışını değiştirerek winner() adlı fonksiyona yönlendirmek.
Burada strcpy() fonksiyonunu kötü emellerimize alet edeceğiz. Nasıl mı? Şöyle ki... ilk strcpy işleminde i1'in hafıza alanını taşırıp i2->name'in tuttuğu adresin üzerine istediğimiz bir adresi yazalım (hatırlayın i2->name bir pointer!!!). Daha sonra'da argv[2] ile o adrese yazmak istediğimiz bir değer gönderelim... Bahsedilen işlemin ardından ikinci strcpy şu hali alır:

strcpy(istedigimiz_bir_adres, istedigimiz_bir_deger)
Burada Global Offset Table 'ın nimetlerinden yararlanıyoruz. Ne olduğunun açıklamasını burada yapmayacağım zira ünlü bir politikacımızın da dediği gibi "hikmetine fazla şeyapmamak lazım". Merak eden olursa gitsin araştırsın efenim.
Öncelikle program içerisinde strcpy'den sonra kullanılan bir fonksiyonu seçiyoruz mesela printf(). Tabiki de burada bilmemiz gereken birşey var. Bir C programı içerisinde printf() kullanıldığında şayet ilk argümanının içerisinde format specifier bulunmuyorsa bu fonksiyon programın derlenmesi sırasında derleyicinin azizliğine (optimizasyonuna) uğrayarak puts() fonksiyonu ile değiştirilir. Velhasıl i kelam burada kullanmamız gereken asıl fonksiyonda puts() 'un ta kendisi. GOT'dan puts() fonksiyonunun adresinin tutulduğu adresi (adres adresi tutarmıymış demeyin, tutuyor!) alıp i2->name'in içerisine bu adresi yazıyoruz. Daha sonra argv[2] ile bunun içerisine yazılacak adresi, yani winner() fonksiyonunun adresini gönderiyoruz. Ardından program printf()'i (yani puts()'u) çağırdığını zannederken aslında bizim winner() fonksiyonumuzu çağırıyor ve exploitimiz başarıya ulaşıyor! Hadi exploitini yazalım..

Offset: 20 (nasıl bulunacağını bilmiyorsan niye okuyorsun ki zaten)


Heap 2

Bunu yanlış sıraya koymuşlar bence Heap 1 falan olmalıydı bu. Neyse..
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <stdio.h>

    struct auth {
      char name[32];
      int auth;
    };

    struct auth *auth;
    char *service;

    int main(int argc, char **argv)
    {
      char line[128];

      while(1) {
          printf("[ auth = %p, service = %p ]\n", auth, service);

          if(fgets(line, sizeof(line), stdin) == NULL) break;
          
          if(strncmp(line, "auth ", 5) == 0) {
              auth = malloc(sizeof(auth));
              memset(auth, 0, sizeof(auth));
              if(strlen(line + 5) < 31) {
                  strcpy(auth->name, line + 5);
              }
          }
          if(strncmp(line, "reset", 5) == 0) {
              free(auth);
          }
          if(strncmp(line, "service", 6) == 0) {
              service = strdup(line + 7);
          }
          if(strncmp(line, "login", 5) == 0) {
              if(auth->auth) {
                  printf("you have logged in already!\n");
              } else {
                  printf("please enter your password\n");
              }
          }
      }
    }
 Bu levelin bütün olayı login sistemini bypass ettirip ekrana "you have logged in already!" yazdırmak. Kodu biraz inceledikten sonra görebileceğiniz üzere "service" komutunu alırken herhangi bir boyut kontrolü yapmamışlar. Yapmamız gereken önce normal bir kullanıcı ile "auth" komutunu çalıştırmak, ardından "service" komutu ile bellek alanını overflow edip auth->auth değerini overwrite etmek.

Heap 3

Burası zurnanın "abi naptın sen" dediği yer. Daha çözmedim, çözünce yazarım. Merak eden olursa level Modifying Metadata in order to alter execution flow konseptinde bi level (hö?).

Sonraki yazıda görüşmek üzere..

Yorumlar

Bu blogdaki popüler yayınlar

DKHOS - Rev300 Çözümü

Part 1: Tersinden Tersine Mühendisliğe Giriş

Bir Güvenlikçi, Virüs ile Karşılaşırsa