池溢出漏洞,还是想说一下这关难度感觉和上一关完全不是一个级别
还涉及到一些简单的池风水
后面这篇文章鸽了去学pwn了,再回来看的时候发现这和堆溢出很像,溢出到下一个池块触发漏洞
先来看源码
ExAllocatePoolWithTag创建一个池块名为Hack
关键点就在于size可控,通过memcpy溢出到下一个池块
可是下一个池块不能确定,所以这里需要用到池喷射
CreateEvent可以在内核池创建一块0x40大小的池块,为了确保Even块和Hack块相邻需要用下面的代码循环创建event对象
1 | HANDLE event1[0x1000] = { 0 }; |
当内核池出现这种情况说明成功了
1 | !pool address |
这样可以查看内存池情况
当然不是说循环多次就必定会在Hack块下面出现,所以需要看点运气,整不好就蓝屏
接下来就是溢出到Even块后能实现什么
1 | kd> dt nt!_POOL_HEADER |
对应2块池块来看看
1 | kd> dt nt!_POOL_HEADER 858bf140 //Hack |
下面是池块size的问题的猜测
1 | 关于几个size位的问题,这里可能有点像堆块结构最后三位有别的用处 |
目前已看到相关文章
PreviousSize和BlockSize存储的块实际大小的右移4位
也就是 size>>4,相当于除以16
所以要得到真实快的大小需要将PreviousSize和BlockSize的值左移4位
然而算了一下感觉应该是右移3位,0x200>>0x3=0x40
那么真实的大小需要左移3位,这里还得打一个问号
这里需要借用一下先知的图
可以看到_OBJECT_HEADER在池块+0x18的位置,同样的代入Event池块
1 | kd> dt nt!_OBJECT_HEADER 858bf340+18 |
InfoMask为0x8代表上面用的是_OBJECT_HEADER_QUOTA_INFO
再代入一下
1 | kd> dt nt!_OBJECT_HEADER_QUOTA_INFO 858bf340+8 |
发现NonPagedPoolCharge为0x40,用的结构正确
其实这里就是根据掩码选择结构
不过这些不重要,重点需要关注TypeIndex
可以用TypeIndex得知池块类型在ObTypeIndexTable的位置
这里TypeIndex=0xc
看下ObTypeIndexTable
1 | kd> dd ObTypeIndexTable |
代入_OBJECT_TYPE结构
上面看着很复杂其实只要找到_OBJECT_TYPE->TypeInfo->CloseProcedure
可以看到当前CloseProcedure为0
CloseProcedure的作用是简单理解就是在close该句柄的时候会优先调用这里的函数,为0就不调用
有点像hook的感觉
结合一下上面的知识点
_OBJECT_HEADER->TypeIndex可以在ObTypeIndexTable找到对应属性
ObTypeIndexTable的第0个指针为0
只需要在溢出的时候修改下一个堆块的TypeIndex为0,系统就会去0x00000000的位置找_OBJECT_TYPE结构,在0地址伪造_OBJECT_TYPE结构
0地址用NtAllocateVirtualMemory申请
此时在0x28+0x38的位置也就是CloseProcedure位置写上指向shellcode的指针,在Event被Close的时候就会触发CloseProcedure位置的shellcode实现提权
当然上面循环了生成了0x1000个Event修改的也不知道是哪个,所以最后要循环全部释放掉
下面就可以开始写代码了,和之前的格式基本一样
创建句柄后输入控制码,Size和Buffer
1 |
|
x64除了一些池块大小和结构位置不一样别的基本相同
Even块为0x80大小
Hack块前0x10用来存放信息还有0x8应该是用来对齐的
1 | kd> dt nt!_POOL_HEADER fffffa8018efbf00 |
相比之下还多了PoolTagHash
_OBJECT_HEADER在堆块+0x30的位置
1 | kd> dt _OBJECT_HEADER fffffa8018efbf00+30 |
CloseProcedure在0x80的位置
知道这些就可以编写64位的exp了
详细的代码就放github好了