漏洞分析丨HEVD-0x4.PoolOverflow[win7x86]

本文详细介绍了Windows内核中的PoolOverflow漏洞,通过分析实验环境、漏洞触发代码,揭示了内核池溢出的原理。利用Event对象在非分页池中的特性,通过创建和释放Event对象来控制内存布局,实现缓冲区溢出并覆盖特定指针以执行shellcode。文章还展示了如何在Windows 7 x86环境下构造EXP进行漏洞利用。
摘要由CSDN通过智能技术生成

作者selph

前言

窥探Ring0漏洞世界:缓冲区溢出之池溢出

实验环境:

•虚拟机:Windows 7 x86

•物理机:Windows 10 x64

•软件:IDA,Windbg,VS2022

漏洞分析
本次实验内容是PoolOverflow,IRP分发函数通过跳转表进行跳转,两项之间的控制码相差4,所以本次实验使用的控制码是:0x22200f,漏洞触发代码:

int __stdcall TriggerBufferOverflowNonPagedPool(void *UserBuffer, unsigned int Size)
{
PVOID PoolWithTag; // ebx

_DbgPrintEx(0x4Du, 3u, “[+] Allocating Pool chunk\n”);
PoolWithTag = ExAllocatePoolWithTag(NonPagedPool, 0x1F8u, ‘kcaH’);// 申请非分页池内存
if ( PoolWithTag ) // 申请成功打印相关信息
{
_DbgPrintEx(0x4Du, 3u, “[+] Pool Tag: %s\n”, “‘kcaH’”);
_DbgPrintEx(0x4Du, 3u, “[+] Pool Type: %s\n”, “NonPagedPool”);
_DbgPrintEx(0x4Du, 3u, “[+] Pool Size: 0x%zX\n”, 0x1F8u);
_DbgPrintEx(0x4Du, 3u, “[+] Pool Chunk: 0x%p\n”, PoolWithTag);
ProbeForRead(UserBuffer, 0x1F8u, 1u); // 确保输入参数地址可读
_DbgPrintEx(0x4Du, 3u, “[+] UserBuffer: 0x%p\n”, UserBuffer);
_DbgPrintEx(0x4Du, 3u, “[+] UserBuffer Size: 0x%zX\n”, Size);
_DbgPrintEx(0x4Du, 3u, “[+] KernelBuffer: 0x%p\n”, PoolWithTag);
_DbgPrintEx(0x4Du, 3u, “[+] KernelBuffer Size: 0x%zX\n”, 0x1F8u);
_DbgPrintEx(0x4Du, 3u, “[+] Triggering Buffer Overflow in NonPagedPool\n”);
memcpy(PoolWithTag, UserBuffer, Size); // 复制输入参数到申请的内存里
_DbgPrintEx(0x4Du, 3u, “[+] Freeing Pool chunk\n”);
_DbgPrintEx(0x4Du, 3u, “[+] Pool Tag: %s\n”, “‘kcaH’”);
_DbgPrintEx(0x4Du, 3u, “[+] Pool Chunk: 0x%p\n”, PoolWithTag);
ExFreePoolWithTag(PoolWithTag, ‘kcaH’); // 释放内存
return 0;
}
else
{
_DbgPrintEx(0x4Du, 3u, “[-] Unable to allocate Pool chunk\n”);
return 0xC0000017;
}
}

乍看之下好像没啥问题,填充缓冲区,同时也限制大小了,仔细一看,emmm,申请内存的大小是0x1F8字节,复制的时候复制大小来自用户输入,是个经典的缓冲区溢出,不过缓冲区是位于非分页池内存

漏洞利用
池风水
内核池类似于用户层的堆,也是用来动态分配内存的。因为是动态分配,所以分配的内存位置就会不固定,在用户层有堆喷射这样的技术来辅助突破动态地址,这里则需要在内核里也找到一种方法来修改内存池,以便在内存区域精准调用shellcode

本例中的程序将用户缓冲区分配在了非分页内存池里,所以需要找到一种方法对非分页池中的地址进行操作以便辅助定位shellcode的执行

Windows提供了一种Event对象,存储在非分页池中,使用API-CreateEventA创建。

根据参考资料[2]中论文的介绍,我们可知:

内核池空闲池块保存在一个链表结构里,当进行申请该池的内存的时候,会从链表里找到合适大小的池块进行分配,如果找不到,则会寻找相近大小的池块进行切割然后再分配;

当空闲链表里有位置相邻的空闲池块,则会进行合并操作,合并成一个大的池块

通过大量申请Event对象,然后通过CloseHandle释放一部分Event对象留出合适的空间给用户缓冲区,那么用户缓冲区很可能就会出现在我们挖出的空缺位置上,并且同时紧紧挨着一个Event对象,也就是说,可以固定让用户缓冲区后面紧挨着一个Event对象

这里需要创建两个足够大的Event对象数组,一个用来消耗小尺寸空闲内存块,一个用来挖出空缺提供给用户缓冲区

在空出的空闲块中,我们将有漏洞的用户缓冲区插入,

图示如下:(参考资料[7])

 

利用原理&Event对象结构
这里的利用方式与之前的堆溢出覆盖堆块链表指针不同,这里通过伪造对象结构来通过堆溢出利用伪造的对象进行执行shellcode(一句话概括:控制缓冲区紧挨着一个Event对象,通过覆盖伪造一个OBJECT_TYPE头,覆盖指向OBJECT_TYPE_INITIALIZER中的一个过程的指针,通过执行该过程从而执行shellcode)具体分析往下看即可

先给一个刚好大小的正常输入看看池的情况:

#include
#include
int main()
{
ULONG UserBufferSize = 0x1f8;
char* UserBuffer = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, UserBufferSize);
RtlFillMemory(UserBuffer, UserBufferSize, 0x66);

HANDLE hDevice = ::CreateFileW(L"\\.\HacksysExtremeVulnerableDriver", GENERIC_ALL, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);

ULONG WriteRet = 0;

DeviceIoControl(hDevice, 0x222003 + 4 * 3, (LPVOID)UserB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极安御信安全研究院

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值