-
-
[翻译]Windows内核漏洞利用教程5:空指针引用
-
发表于:
2018-1-22 11:25
4890
-
[翻译]Windows内核漏洞利用教程5:空指针引用
原文地址:https://rootkits.xyz/blog/2018/01/kernel-null-pointer-dereference/
该系列已翻译文章
在这个系列中,在
上一篇详细的文章之后,本文将分析一个理解起来相对简单的漏洞类型。空指针引用漏洞,当一个指针的值为空时,却被调用指向某一块内存地址时,就产生了空指针引用漏洞。显然,问题很清楚,如果我们可以控制NULL page,并且在那里写入东西,我们就可能得到代码执行权限。你可能已经猜到,我们将使用和上一篇文章一样的方法去申请NULL page,布局我们的shellcode。所以这一片文章中的很多信息将依赖上一篇文章。
NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
ULONG UserValue = 0;
ULONG MagicValue = 0xBAD0B0B0;
NTSTATUS Status = STATUS_SUCCESS;
PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
PAGED_CODE();
__try {
// 检查buffer是否属于用户态
ProbeForRead(UserBuffer,
sizeof(NULL_POINTER_DEREFERENCE),
(ULONG)__alignof(NULL_POINTER_DEREFERENCE));
// 申请pool 块
NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(NULL_POINTER_DEREFERENCE),
(ULONG)POOL_TAG);
if (!NullPointerDereference) {
// 申请失败
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);
}
// 获取用户态传进来的值
UserValue = *(PULONG)UserBuffer;
DbgPrint("[+] UserValue: 0x%p\n", UserValue);
DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
//检查magic值
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);
// 释放申请的pool块
ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
// 设置指针为空
NullPointerDereference = NULL;
}
#ifdef SECURE
// 安全代码:在调用回调函数之前,检查指针是否为空
if (NullPointerDereference) {
NullPointerDereference->Callback();
}
#else
DbgPrint("[+] Triggering Null Pointer Dereference\n");
// 不安全代码:在调用回调函数之前,没有检查指针是否为空
NullPointerDereference->Callback();
#endif
NTSTATUS TriggerNullPointerDereference(IN PVOID UserBuffer) {
ULONG UserValue = 0;
ULONG MagicValue = 0xBAD0B0B0;
NTSTATUS Status = STATUS_SUCCESS;
PNULL_POINTER_DEREFERENCE NullPointerDereference = NULL;
PAGED_CODE();
__try {
// 检查buffer是否属于用户态
ProbeForRead(UserBuffer,
sizeof(NULL_POINTER_DEREFERENCE),
(ULONG)__alignof(NULL_POINTER_DEREFERENCE));
// 申请pool 块
NullPointerDereference = (PNULL_POINTER_DEREFERENCE)
ExAllocatePoolWithTag(NonPagedPool,
sizeof(NULL_POINTER_DEREFERENCE),
(ULONG)POOL_TAG);
if (!NullPointerDereference) {
// 申请失败
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);
}
// 获取用户态传进来的值
UserValue = *(PULONG)UserBuffer;
DbgPrint("[+] UserValue: 0x%p\n", UserValue);
DbgPrint("[+] NullPointerDereference: 0x%p\n", NullPointerDereference);
//检查magic值
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);
// 释放申请的pool块
ExFreePoolWithTag((PVOID)NullPointerDereference, (ULONG)POOL_TAG);
// 设置指针为空
NullPointerDereference = NULL;
}
#ifdef SECURE
// 安全代码:在调用回调函数之前,检查指针是否为空
if (NullPointerDereference) {
NullPointerDereference->Callback();
}
#else
DbgPrint("[+] Triggering Null Pointer Dereference\n");
// 不安全代码:在调用回调函数之前,没有检查指针是否为空
NullPointerDereference->Callback();
#endif
在代码中,用magic值(0xBAD0B0B0)和用户态的值相比较,如果不相同,NullPointerDereference指针被设置为空。我们可以看到,在安全的代码中,检查了NullPointerDereference指针是否为空。
从IDA简单分析可以看出,非分页pool的Tag 为“Hack”,magic值,以及偏移0x4,我们要写入shellcode的地方。
这次要使用的IOCTL值为0x22202b.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课