SEH Based Exploits

Structured Exception Handlers

Simge Karahan
5 min readJan 5, 2021

Exception istisna anlamına gelir, Exception handler ise hata işleme amacıyla yazılan bir kod parçasıdır, yapısı şu şekildedir:

try
{
//Buradaki kodda hata olursa catch bloguna gider
}
catch
{
//Hata durumunda çalıştırılacak fonksiyon
}

Windows işletim sistemlerinde exceptionları yakalayan default bir SEH vardır. “Bir sorunla karşılaşıldı ve uygulamanın kapatılması gerekiyor” yazılı bir popup açıldığını hepimiz görmüşüzdür, işte bu durum bir exception yakalandığında gerçekleşir. Varsayılan handlerın devreye girmesinin bir sonucudur. Windows’daki varsayılan handlera güvenmemeliyiz, uygulamamızın yazılım diline özgü exception handlerları kullanmalıyız. Bir hata meydana geldiğinde kodda bizim yazdığımız exception handlers yoksa işletim sistemi devreye girer ve uygulama hatayı işleme şansı yakalar, hata yakalanıp popup penceresinde gösterilir.

Uygulamanın yakalama koduna gidebilmesi için exception handlersın pointerı Stack’e kaydedilir. Her kod bloğunun kendi Stack alanı vardır ve exception handlersın pointerı da bu Stack’in bir parçasıdır. Stack üzerinde exception_Registr yapısında saklanır. SEH kaydı 8 bayttır ve 2(4 bayt) bileşenden oluşur:

  1. Bir sonraki SEH pointerı ( geçerli işleyicinin yani 2. maddede olanın hatayı işleyememesi durumu için)
  2. SEH adresini gösteren pointer

Bunlar Stack’te alt alta exception_handler(1)- exception_handler(2)- exception_handler(3)-msvcrt!exhandler şeklinde bir zincir (SEH Chain) olarak bulunur. FS:[0] ile bu zincirin başı işaret edilir yani exception_handler(1). Zincirin en sonundaki ise işletim sistemi tarafından sağlanan SEH ve kayıt adresi 0XFFFFFFFF’dir.

Not: Intel makinelerinde SEH koduna bakınca move DWORD ptr from FS:[0] talimatını görebiliriz. Bu talimat, exception handlerın hataları yakalayabilmesini sağlar. Bu talimatın opcode’u 64A100000000’dir.

Immunity Debugger’da SEH zincirini görüntülemek için “view” sekmesinin altından “seh chain” bölümüne gidebiliriz. Örnek bir seh chain şekilde gösterilmektedir:

Şekilde de görüldüğü gibi en sonda default seh (kernel32.) vardır, adresine gidersek 0XFFFFFFFF olduğunu görürüz:

SEH chain başlangıcına gidersek orada bir sonraki exception handlerın bulunduğu adresi ve bir altında mevcut olan exception handlerın adresini görürüz. Bu ikili yapı ikili seh kaydı yapısı olarak bahsettiğim 4bayt+4bayt şeklindeki 8 baytlık yapıdır. Zincirin bir halkasıdır:

SEH zincirinde görülen bir sonraki adres olan 0012FEF8 adresine gidersek yine aynı şekilde bir sonraki seh ve mevcut seh’den oluşan ikili yapıyla karşılaşırız. İlk olarak bir sonraki seh adresi yazar yani 0012FEF8. 012FEF8 adresinde ise bir sonraki seh pointerı yani 012FFB0 yazar. Eğer fonksiyonda exception yakalanmazsa, listede ilk yazan adres yani bir sonraki seh adresinin olduğu yerden adresi alınır ve bir sonraki adrese gidilir. Böyle böyle tüm exception handlerlar kontrol edildikten sonra hala hata yakalanmamışsa Windows’un varyasılan exception handlerı devreye girer ve “bir hatayla karşılaşıldı” yazılı popup açılır.

SEH tabanlı zafiyeti nasıl kullanabiliriz?

Bir exception oluştuğunda işletim sistemi gelip SEH’e bakıyor. Çünkü buradaki kodlar çalışacak, eğer exception yakalanmazsa next exception handler adresine gidecek. İşte buraya Shellcode’u gösteren adres yazılırsa exception durumunda işletim sistemi Shellcode’u çalıştıracak.

Payload yapısı: [Junk][nSEH][SEH][Nop-Shellcode]

Nseh (Next SEH) = Shellcode’a atlama kodunun yazılması amaçlanan yer

SEH overwrite için universal bir adres seçilmeli. İdeal olanı uygulamanın kendi dll’lerinden iyi bir sıra bulmaya çalışmaktır. Bu sıra pop pop ret yapısıdır.

Stackte Esp+8 adresine bir sonraki seh pointer’ının adresi yazılmakta, bu da bir sonraki exception handler’ı göstermektedir. +8'e yazıldığı için iki pop ve ardından ret komutları kullanılmaktadır. Ret çalıştırıldığında, Eip’e bir pointer yazılıyor, bu pointer bir sonraki seh adresini (overwrite edebildiğimiz alan) gösteriyor. SEH’in çalışma yapısından dolayı buraya 4 baytlık bir komut yazılabiliyor ve buraya yazılan komutlar ise istediğimiz yere jumpcode komutlarıdır.

Örnek: Soritong mp3 player uygulaması ve olldbg kullanılmaktadır.

Ollydbg: Bir hata ayıklayıcısıdır. Kayıtları izler, prosedürleri, API çağrılarını, anahtarları, tabloları, sabitleri ve dizeleri tanır ve nesne dosyalarından ve kütüphanelerden gelen rutinleri bulur.

Overflow’a neden olacak python kodunda ui.txt adlı bir dosya oluşturulup içine fazla miktarda A harfi yazılmaktadır. Adının ui.txt olmasına dikkat etmeliyiz çünkü bu dosyayı Soritong’un skin\default\ dizinindeki ui.txt dosyasıyla değiştireceğiz:

Oluşan ui.txt doyasını Soritong’daki ui.txt ile değiştirip Ollydbg’da Soritong uygulamasına bakalım:

Uygulamanın 0x0042E33’de (sağ üst kısımda Eip) öldüğünü görüyoruz. Bu noktada Esp 0x0012DA14 adresini gösteriyor.

Seh zincirinin sonu ise 0012DA6C’de (sağ alt kısımda FFFFFFFF), bunun hemen altında varsayılan seh adresi olan 7E41882A var, bu adres user32.dll’de bulunur. Stack’te biraz yukarılara bakıldığında diğer seh’i görmekteyiz, bu da işletim sistemine ait bir dll (ntdll).

Bu durumdan şunu anlıyoruz, bu uygulamanın kendi exception handlerı yok, işletim sisteminin varsayılanlarını kullanıyor.

View sekmesinin altından threadları görüntüleyip baştaki ifadede sağ tıklayıp “dump thread data block” seçeneğiyle seh zinciri pointerını görebiliriz:

SEH Cahin Pointer

Hatalı oluşturulmuş bir ui.txt dosyası ile uygulama içinde bir exceptiona neden olduk, exception çalıştı ve uygulama seh zincirine atladı (0x0012DF64). View sekmesinden seh chaine baktığımızda:

Seh adresi exception ile başa çıkmak için çalıştırılması gereken kodun adresini işaret eder. Seh üzerine AAAA ile overwrite edildi. Seh adresi Eip’e yazılacak(eip akışta bir sonra çalıştırılacak komutun adresini tutan register). Handlerdaki değeri kontrol edebildiğimizde kendi kodumuzun çalıştırılmasını sağlayabiliriz.

Seh tabanlı istismarlarda, junk payload önce “bir sonraki seh pointer adresinin” ardından Seh’in üzerine yazılır(overwrite işlemi) sonra da shellcode yazılır. Exception oluştuğunda uygulama Seh’e gidecek dolayısıyla Seh üzerine Shellcode’a gidecek bir şey koyulmalı. Bu işlemi, ikinci bir exception taklit edilerek yapılır böylece uygulama bir sonraki seh pointerına gider. Bir sonraki Seh pointerı Seh’in önünde olduğundan bir sonraki Seh overwrite edilir. Shellcode Seh’den sonra bulunur. Seh’de pop pop ret talimatı çalıştırılabilirse Eip’e bir sonraki Seh adresini koyar ve bu da sonraki Seh’de çalışır, bu nedenle sonraki Seh’e bir adres koymak yerine kod koyulur. Koyulan kodun yapması gereken Shellcode’a atlamaktır. Ve shellcode çalışır.

Kaynaklar:

https://en.wikipedia.org/wiki/OllyDbg

https://www.corelan.be/

--

--