首页
社区
课程
招聘
[原创]初探内核漏洞:HEVD学习笔记——BufferOverflowNonPagedPool
发表于: 2022-7-18 14:30 8528

[原创]初探内核漏洞:HEVD学习笔记——BufferOverflowNonPagedPool

2022-7-18 14:30
8528

强烈建议先浏览以下推荐文章,再来学习HEVD的非分页池溢出漏洞!

https://www.anquanke.com/post/id/219839

https://www.freesion.com/article/2629792577/

反汇编IrpDeviceIoCtlHandler函数后可以清楚地看到HEVD驱动的分发流程。其中,0x22200F就是我们本次关注的IO控制码:
图片描述
跟进触发池溢出漏洞的函数TriggerBufferOverflowNonPagedPool,可知该函数使用ExAllocatePoolWithTag在非分页内存池中分配了大小为0x1f8(504)的池空间。然后使用ProbForRead检测传入的Userbuffer是否属于用户空间,然后使用memcpy将UserBuffer在不限制大小的情况下直接将用户输入复制进该空间,有缓冲区溢出的隐患。最后使用ExFreePoolWithTag释放申请的池空间。
图片描述

我们先将用户缓冲区大小设置为0x1f8来验证一下内存拷贝的过程,使用Windbg在TriggerBufferOverflowNonPagedPool处下断点,执行客户端程序,触发断点:
图片描述
图片描述
可知TriggerBufferOverflowNonPagedPool的地址为959eccce,反汇编找到调用memcpy的代码地址为959ecdc5
图片描述
图片描述
在此处下断,继续运行,触发断点:
图片描述
局部变量KernelBuffer的地址为0x8760f118:
图片描述
我们可以使用命令!pool address查看address周围地址处的池信息:
图片描述
8760f110就是TriggerBufferOverflowNonPagedPool中申请到的池的起始地址,大小为0x200,所以末尾地址为8760f110+200=8760f310,这也是下一个池块的头部所在地址。查看memcpy运行后该地址处的内存:

图片描述
我们的目标就是构造数据,修改下一个池块的内容,从而达到运行我们的shellcode的目的。具体覆盖什么内容,使用什么执行机制,将在漏洞利用中详细分析。

池块分布是无序的,而我们希望能在非分页内存池中控制池块分布,使得溢出点所在池块后边是我们构建的池块,从而达到溢出利用条件。

我们知道存储在非分页池中的通用系统数据结构包括表示进程和线程的内核对象,互斥对象,信号量和事件等同步对象,表示为文件对象的文件引用以及I/O请求包(IRP)代表I/O操作。

所以,我们可以利用CreateEventA生成Event事件对象,把用event对象将池铺满,然后CloseHandle释放一些对象来控制池块分布。每个Event对象大小为0x40,总共需要0x1f8+0x8=0x200(其中0x8是下一个池块的POOL_HEADER),相当于8个event的空间。我们可以每间隔8个event释放一个event来保证池空间分布。客户端代码如下:
图片描述
图片描述
运行以上客户端程序,查看此时的池块情况:
图片描述
可以看到我们构建的event块刚好在函数申请的池空间下边,进而达到溢出利用条件。

复习一下Win7池块的结构图:
图片描述
我们看看每种结构的成员:
图片描述
只有OBJECT_HEADER_QUOTA_INFO的+0x4偏移处存在NonPagedPoolCharge成员指定pool的大小,所以POOL_HEADER后面是OBJECT_HEADER_QUOTA_INFO结构。
查看下一个池块的数据对应结构:
图片描述
查看每个结构具体成员的数据:
图片描述
我们知道,Win7之后,OBJECT_HEADERTypeIndex成员的值是一个索引值,由ObGetObjectType函数根据索引值在ObTypeIndexTable数组中找到对应的OBJECT_TYPE结构的起始地址:
图片描述
上图我们根据0xc的TypeIndex定位到我们的OBJECT_TYPE起始地址为86a229c8。查看此处的结构数据,点击TypeInfo成员:
图片描述
TypeInfo是一个OBJECT_TYPE_INITIALIZER结构体,我们关注其中的CloseProcedure字段,在池块释放时会调用这里的代码,所以我们将此字段覆盖为shellcode的地址即可。

win7中,我们可以在用户模式下控制0页内存,所以我们将OBJECT_HEADER的TypeIndex索引从0xc修改为0x0
图片描述
构造池块数据时,由于只需修改TypeIndex,它相对于池块起始位置的偏移是0x8+0x10+0xc=0x24,大小为4,所以溢出数据长度为0x28:
图片描述
然后在0页+0x60(OBJECT_TYPE+0x28+0x38=0+0x60=0x60)处写入shellcode的地址就能达成利用。
下面我们申请0页内存空间。首先要获取NtAllocateVirtualMemory函数地址:
图片描述
获取到之后,BaseAddress设置为1是因为:如果设置为0,系统就随机分配基址,不能达成我们要0页内存的目的;设置为1,由于对齐,就会分配到0页内存:
图片描述
申请到0页内存之后,在+0x60处写入shellcode地址:
图片描述
至于shellcode的内容,需要通过动态调试来维持堆栈平衡:
图片描述
运行客户端程序:
图片描述
可以看到,我们获得了system权限。

这篇笔记是本人参考了许多大佬的文章,自己动手实践并记录的结果,在此感谢:

 
 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//