C/C++ Güvenli Kod Geliştirme - Heap Overflow, Memory Leak, Use After Free

Pentester

Özel Üye
8 Eyl 2016
1,646
996
Merhabalar, bir önceki bölümde stack yapısı üzerine konuşmuştuk ve belleğin temelde iki bölümünden biri olan stack üzerine konuşmuştuk. Şimdi bu konuda ise heap üzerine konuşarak stack ve heap arasındaki fark nedir, neden heap kullanırız, dinamik bellek yönetimi nedir konularına ve heap overflow, memory leak, dangling pointerdan kaynaklı use after free zaafı üzerine konuşacağım.

Stack Buffer overflow serinin ilk konusuna gitmek için C Güvenli Kod Geliştirme - Stack Overflow Zafiyeti


HEAP nedir?

Yukarıda belirttiğim gibi ram’de değerlerimiz stack ve heap bölgelerinde tutulur. Biz genellikle stacki kullanırken statik olarak belirli sayıda byte alan tahsis ederiz bu alan stackte ayrılır ve bu alanın üzerine çıkamayız. Yani aslında boyutu belirli bir alanımız varsa stack kullanıyoruz da diyebiliriz. Ancak Heap tarafına geçtiğimiz zaman karşımıza dinamik bellek yönetimi kavramı çıkıyor. Yani değerlerimizi heap de tutacaksak bu alanımız bizim için ram üzerinde değişebilir bir şekilde yer kaplayan bir alan olabilir. Yani örnek verelim kullanıcıdan belirli bir sayı listesi alacaksınız ve bu sayıları ram’de tutmak için belirli bir alan ayırmanız gerekiyor. Ama burada ayrılacak alan miktarı sabit değil kullanıcının kontrolünde o sebepten sınırlı bir kapasite ayırırsanız istenmedik sonuçlar doğurur bu işin dinamikliği ortadan kalkar. O yüzden kullanıcı 5 değer girecekse ona göre otomatik alan ayrılması gerekiyor.

Şunu unutmayın stack derleyici tarafından yönetiliyor, heap ise geliştirici tarafından yönetilir o yüzden dinamik bellek yönetimi konusu burada devreye girer.


Memory Leak

Memory leak yani bellek sızıntısı olayını anlamak için öncelikle buradaki olayı stack ile kıyaslamak istiyorum.

6fnr3dl.png


Şimdi main fonksiyonun içerisindeyim yani main scope sınırlarındayım. 14. Satıra yani main alanının köklü parantezlerle son noktasına breakpoint yani durma noktası koyup uygulamamı derliyorum.


cx17eb7.png


Bu durumda stack alanımda x değerinin tutulduğunu görmekteyim. Gayet normal bir durum değil mi şimdi bir diğer görselimize bakalım.

lmhl049.png


Bakın yine main alanının bittiği noktaya breakpoint atıp stackte x değerimin varlığını kontrol ettiğimde locals alanında göstermediğini görüyorum. Peki bu neden oluyor? Koda dikkatli bakarsanız x değişkeni main içerisinde ama artık farklı bir scope içerisinde yer alıyor çünkü ayrı bir köşeli parantezler arasında duruyor.

j86jxtg.png


Bakın programımı 12.satırda durdurduğumda ilgili scope da verimin stack içerisinde yer aldığını görüyorum. Buradan şunu anlamamız gerekiyor stackte verilerimiz kapsam alanı içerisinde push edilir kapsamı dışına çıkıldığı zamana pop edilir yani stack içerisinden temizlenerek gereksiz olarak ram üzerinde yer tutmaz. Şimdi de heap ile verilerimizi yönetelim.

q2ctkby.png


Şimdi burada int tipinde dizi adında değişken tanımladım. Kullanıcıdan eleman sayısını alıyorum ve dizi değerimi dinamik olarak calloc ile yönetiyorum. İlk parametre olarak n değerini veriyorum yani eleman sayısını, ikinci parametre olarak ise bu elemanların veri tipini int olarak veriyorum. Yani her bir eleman için int tipinde 4 byte yer tahsis edilecek. Programı derleyip 3 eleman sayısını verdiğimde ram’de 3 elemanlık yer ayırıyor ve başlangıç değerlerini 0 olarak veriyor. Yani biz değerleri henüz girmedik sadece eleman sayısını verdik ve dinamik olarak 3 eleman için yer tahsis etti ve bunlara başlangıç değeri atadı. Biz değerleri doldurduğumuz zaman bu 0 değerlerinin yerini bizim değerlerimiz alacaktır.

qk3v2rw.png


Programımı 21.satırda durudurup incelediğim zaman hafızada dizimin değerinin 0 olduğunu, n ve i içerisinde de 3 eleman tutulduğunu görebiliyoruz.

qttmd4w.png


Şimdi aynı yapıyı calloc yerine malloc ile yaparken malloc tek bir parametre alıyor. Eleman sayısını veri tipi ile çarpıyoruz ve ona göre alan tahsis ediyor ve sonucuna bakarsak 0 değerleri görünmüyor. Yani malloc ilgili alanlar için başlangıçta bir değer ataması yapmıyor. Burada kısaca malloc ve calloc kullanıcıdan alınan değere göre bellekte alan ayırdı. Şimdi farklı bir örnek yaparak memory leak olayını inceleyelim.

Stack içerisinde bir veri işlendikten sonra kendini siliyordu yani yer kaplamaya devam etmiyordu. Ancak bu durum heap yapısı ile veriyi yönettiğimizde bu şekilde işlemiyor. Burada belleği yönetirken yazılımcının bu ayrıntıya dikkat ederek ilgili alan tahsis edilip işlem tamamlandıktan sonra ilgili alanın temizlenmesi gerekiyor.


mh7s9zh.png


Şimdi burada neler yaptım açıklayayım. Öncelikle POINT adında bir struct oluşturup iki tane değer verdim. Main içerisinde de malloc ile struct değerlerinin dinamik olarak alınmasını sağladım ilgili değerleri 20 ve 21.satırlada verdikten sonra değerleri ekrana yazdırdım. Gördüğünüz gibi ne oldu? Ekrana yazılan veriler tanımladığımız veriler gayet normal. Daha sonrasında bu free ile bu ayrılan bellek alanını boşalttım ve değerleri tekrar yazdırdığımda alakasız değerler gördüm ve buradan anlaşılacağı üzere ilgili bellek alanını free ile boşaltmış oldum ve memory leak olayının önüne geçtim.

Burada memory leak olayını bir de C++ uygulaması üzerinde göstereyim.


rb71fk6.png


Burada X adında bir sınıf oluşturup X’in oluşturulduğu anı ve silindiği anı tanımlıyorum. Sonrasında da main içerisinde bir scope oluşturup bu alan içinde X sınıfından a adında bir değişken obje oluşturarak kullanıyorum ayrıca köklü parantezler öncesi ve sonrasında da scope öncesi ve sonrası mesajlarını da yazdırıyorum.

hm3kq6y.png


Aldığım çıktı da bu şekilde oldu. Yani ne oldu? Programımız main fonksiyonu içerisinde satır satır işlenmeye başladı. Karşısına çıkan ilk mesajı bastırdı. Sonra ilgili scope içerisine girerek X sınıfından türetilen nesne vasıtasıyla oluşturulduğu anı yakaladı işi bitince silindi deyip scope’un dışına çıkarak scope sonrasını yazdırdı. Yani scope sonlanınca destructor metodu çağırarak nesneyi silip yoluna devam etmiş oldu. Bunlar gayet normal, belirli bir scope dışına çıkınca stackten veriler pop edilir biliyoruz. Şu an burada heap yapısı yok, dinamik bellek yok yani bu veriler stackte tutuluyor.

Şimdi bunu heap üzerinde tutalım.


gcpwh23.png


Burada new anahtar kelimesini kullanarak değerimizi heap alanında tutuyoruz. Şimdi sonucu görelim.

38xtaal.png


Bakın ne oldu? Scope öncesine geldi, scope’a girdi ve nesneyi oluşturdu scope dışına çıktı ama nesneyi silen destructor metodunu işlemeden scope sonrasına geçti. Yani scope dışına çıkması veriyi silmesini sağlamadı çünkü heap’de bunu bizim yani geliştiricinin silmesi gerekiyor. Stackte tutulduğunda derleyici bizim yerimize yapıyordu. İşte burada gördüğünüz gibi verimizi heap’de tutarken bir adet memory leak zafiyeti bırakmış olduk. Bu bellek alanı tahsis edildi ancak işi bitince silinmediğinden yer kaplamaya devam ediyor. Bu da ciddi bir performans sorununa yol açacaktır.

rp7yarl.png


Burada heap de değeri tanımladıktan sonra p değişkeni ilgili değerin point ettiği adresi tutuyor ve bunu delete ederek silinmesini sağlıyoruz ve çıktıya bakarsak nesne oluşuyor, scope dışına çıktığı anda siliniyor.

Heap Overflow Zafiyeti

Heap overflow, yine stack overflow da olduğu gibi bellek yönetimi için ayrılmış alanın taşması sonucunda meydana gelen bir zafiyettir.

dvgw18b.png


Bununla alakalı basit bir uygulama yazdım. Şimdi ilgili kod bloklarımızı yorumlayalım. Char tipinde 200 byte yer tutan source değişkenimiz var. Bu source değişkeni stack bölgesinde tutuluyor. Hemen altında ise char tipinde dest değişkeni var ve malloc ile heap de 10 byte yer tutuyor. Kullanıcıdan fgets metodu ile alınan değerler source değişkeninde yani stackte tutuluyor. Dikkat ederseniz fgets ile alınıyor ve source değişkeninin kapasitesi 200 byte alanı aşmayacaktır çünkü fgets aldığı ikinci parametrede bunu denetliyor. Yani burada stack tabanından kaynaklanacak bir zaafın önüne geçilmiş bulunuluyor. Ancak olay burada bitmiyor tabii. Strcpy metodu source içerisinde tutulan verileri dest alanına yani heap’de 10 byte ile sınırlanmış alana kopyalayacak. E source 200 byte’a kadar veri alabilir ama dest sadece 10 byte’a kadar alır ve dest’e 10 bytedan daha büyük bir değer girdi olarak verilirse heap tabanında bir taşma meydana gelmiş oluyor.

mm1qzx3.png


Gördüğünüz gibi çok fazla bir a karakteri gönderdim belki 500 tane gönderdim ama bilmemiz gereken şu ki source 200 byte sınırını koruyabiliyor yani günün sonunda 1000 tane de göndersem source en fazla 200 byte alacak ve heapdeki dest değişkenine de en fazla 200 byte yazacak ki bu da yine uygulamanın heap overflow’a uğraması için yeterli bir neden oluyor.

mggik17.png


Şimdi ise uygulamamı güvenli hale getirmek için strncpy metodunu kullanarak 3.parametre olarak da heap içerisinde tutulacak veriminde source içerisinden gelecek 5 karakteri kapsamasını söylüyorum ve 10 byte sınırını aşmayarak overflowdan kaçınıyorum. Aynı zamanda heap üzerinde verimi kopyalayıp işlemimi sonlandırdıktan sonra memory leak önlemi içinde free ile alanımı boşaltıyorum.


Use After Free Zafiyeti

Şimdi bu zafiyeti teoride açıklayayım. Bir mesajı saklamak için ptr adında bir işaretçi döndüren malloc kullanarak bir bellek ayırdık. Daha sonra ptr’nin gösterdiği adreste depolanan verileri kullandık işimiz bitince de free ile temizledik. Sonrasında yine malloc kullanarak ptr2 pointerını oluşturduk. Bu bilgide az önce boşaltılmış ptr pointerının işaret ettiği adresin bulunduğu bölgeye yazıldı ve ilgili bloğun adresi de ptr2ye döndürüldü. Yani ptr ve ptr2 aynı blok adresini depolamaya başlıyor. İşte bu olaya da use after free diyoruz.

r0uh6is.png


Bakın malloc ile heap üzerinde 5 adet integer değer tahsis ettikten sonra free ile boşaltıyorum. Hemen ardından malloc ile bir adet integer değer daha heap de oluşturduktan sonra oluşan son değerin yani 126 değerinin bellekteki adresine dikkat ederseniz freelenen ilk bellek adresiyle aynı adresi gösterdiğini görebilirsiniz. Yani şu an aynı bellek adresini işaret eden iki farklı değer var. İşte use after free olayının uygulamadaki gösterimi de bu oluyor. Yani malloc bellek adresini tahsis ediyor, free ise tahsis edilen bellek adresini boşa çıkarıyor. Buradaki olaya dangling pointer da deniyor. Yani ip pointerımız şu an dangling pointer oluyor. Free ile alanı boşalttık ancak boşaltılmış bir bellek adresini işaret etmeye devam ediyor.

54kh3pe.png


Bakın kırmızı ile işaretlediğim adreslere yani 1 ve 126 değerleri aynı bellek adresini işaret etmeye devam ediyor. Burada yapmamız gereken şey bellek adresini boşa çıkarmanın yanı sıra işaretçinin değerini de NULL olarak güncellememiz gerekiyor.

rn9u9kf.png


Freele, sonra NULL olarak güncelle.

Okuduğunuz için teşekkürler.

 
Son düzenleme:

KaptanTR

Alpha Wolf
17 Nis 2015
1,650
1,583
25
Mükemmel bir bilgilendirici kaynak olmuş. Sayenizde güzel bilgiler ediniyoruz. Elinize, emeğinize sağlık. 🙏
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.