查看原文
其他

HEVD池溢出系列

某警官 看雪学苑 2022-07-01

本文为看雪论坛优秀文章

看雪论坛作者ID:某警官



这个漏洞的复现过程实在是太过于精美,并且在这过程间补充到了不少的知识,打算就池溢出做一期笔记。


一  堆溢出到池溢出


内核池实际上是windows中类似于堆的一种动态内存结构。两者的利用在我看来是十分相似的。
 
将池溢出之前,我们不妨先回顾以下堆溢出:
 
有CTF经验的朋友们应该不陌生堆溢出,那么如何完成一次堆溢出利用呢?
 
可以做出如下的假设:
 
堆块是系统分配给程序的空间,堆块在系统中是连续的一段内存,那么如果我们可以为某个带函数指针的对象申请一段内存空间,并且通过堆溢出修改我们的函数指针指向shellcode,那么我们即可完成堆溢出利用。
 
 
但是理想和现实总是存在差距的,好在可以将理想照进现实。那么不妨从这个思路开始。
 
按照我的思路,分析漏洞时需要有三个部分的思考
  • 它是谁:函数是哪里出现问题的,我们可以判断出它是哪种类型的漏洞。

  • 到哪里去:函数会导致什么后果,从而触发漏洞
  • 从哪里来:如何去调用到目标函数。

但是由于HEVD本身就是一个漏洞复现的靶场,由于存在源码,所以我们可以省去最后一步做分析。
 
但我认为,复现或者挖掘漏洞的过程中,从哪里来也是很重要且具有挑战性的工作。



二  它是谁?


使用IDA定位到漏洞位置。

那么我们先简单的编制一个poc,证明漏洞的存在。

#include<stdio.h>#include<windows.h>
int main() { ULONG bReturn = 0; HANDLE hevDevice; ULONG BytesReturned; hevDevice = (HANDLE)CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", 0xC0000000, 0, NULL, 0x3, 0, NULL); char buf[0x1f8] = { 0 }; if (!hevDevice) { printf(" failed to get handle"); } RtlFillMemory(buf, 0x1f8, 'a'); DeviceIoControl(hevDevice, 0x22200f, buf, 0x1f8, NULL, 0, &bReturn, NULL);
return 0;}

在之前的分析中,我们已经知道了是由于HEVD!memcpy没有对size进行判断从而导致了漏洞的发生,那么我们首先通过windbg下断点到HEVD!memcpy。

运行我们的Poc
kd> u HEVD!TriggerBufferOverflowNonPagedPool+0xf0HEVD!TriggerBufferOverflowNonPagedPool+0xf0 [c:\projects\hevd\driver\hevd\bufferoverflownonpagedpool.c @ 137]:839f3dbe ff750c push dword ptr [ebp+0Ch]839f3dc1 ff7508 push dword ptr [ebp+8]839f3dc4 53 push ebx839f3dc5 e812c4fbff call HEVD!memcpy (839b01dc)839f3dca 6886659f83 push offset HEVD! ?? ::NNGAKEGL::`string' (839f6586)839f3dcf 6a03 push 3839f3dd1 6a4d push 4Dh839f3dd3 ffd7 call edi
kd> u HEVD!TriggerBufferOverflowNonPagedPool+0xf7HEVD!TriggerBufferOverflowNonPagedPool+0xf7 [c:\projects\hevd\driver\hevd\bufferoverflownonpagedpool.c @ 137]:839f3dc5 e812c4fbff call HEVD!memcpy (839b01dc)839f3dca 6886659f83 push offset HEVD! ?? ::NNGAKEGL::`string' (839f6586)839f3dcf 6a03 push 3839f3dd1 6a4d push 4Dh839f3dd3 ffd7 call edi839f3dd5 681a619f83 push offset HEVD! ?? ::NNGAKEGL::`string' (839f611a)839f3dda 688e649f83 push offset HEVD! ?? ::NNGAKEGL::`string' (839f648e)839f3ddf 6a03 push 3kd> bp HEVD!TriggerBufferOverflowNonPagedPool+0xf7

查看池状态,当然,之前我们下了断点,此时记得执行p命令,查看当前池块内容:

试着多发送8个字节的内容。

我们发现后续的池块由于结构被破坏,导致了无法被识别。
此时系统也会发生蓝屏。

此时我们已经彻底明白了,漏洞“是谁”。

 
但是,我们简单布置的poc,多溢出的字节会由于破坏的后续池块的结构而导致系统的BSOD。
 
也就是,我们的“到哪去”并不顺利。那么,我们应该更具体地构想,这段内存应该到哪里去。


三  到哪里去?


根据之前所讲的,由于C++面向过程的特性,我们可以向申请的堆块(池)中为某些特定对象申请空间,进而通过溢出控制其函数指针从而执行shellcode。
 
当然,对象的大小也必须是合适的,由于我们的池块大小为0x1F8+8=0x200
 
因此选择CreateEventA函数,CreateEventA申请出Event对象的大小为0x40,0x40 * 8 == 0x200。
 
先申请大量的event对象,再释放掉部分的event,这将会在内存中造成一些空洞,从而得以在这段空间中申请到kernel buffer,此时再申请kernel buffer将会保证下一个位置是一个Event对象。 
申请大量的event对象后:

那我们随便取出一个event对象查看内部结构,首先我们查看windows中object的结构。
index的值为0xC,所在obTypeIndexTable表中的偏移为0xC。
所谓obTypeIndexTable表是指,xp直接在OBJECT_HEADER里保存了POBJECT_TYPE指针,而Win7里把所有的对象类型放在了一个表里,这个表叫做obTypeIndexTable。

查看该表内容,并定位到0xC的位置。

可见Event对象保存在此处,查看该对象结构。


这里我们选用CloseProcedure函数作为修改的函数,理由是,在之后的源码中,我们发现会有:

 

四 • 函数来释放这段空间


可是接下来的问题,怎么能够去修改到CloseProcedure函数的呢?我们并不能通过池溢出直接修改到这个函数的指针。
 
但是我们能修改到index的索引。
 
ObTypeIndexTable第0个索引指向0地址(0x00000000)。
 
利用此思路,我们可以构建如下的利用思路:
 


利用:


exp写的不是很优雅,建议从参考链接中获取两位师傅的博客。


道别时想说些什么


拖了很久才完成这一部分的利用,emmmm,总体收获还是蛮大的,离不开50u1w4y师傅的帮助,exp也是参考了他的exp,故而就不张贴出来了。
 
当然复现的过程中也从这些师傅的博客中获取了很多知识,十分感谢他们。

参考链接:

  • https://50u1w4y.github.io/site/HEVD/nonPagedpooloverflow/#0x00
  • https://bbs.pediy.com/thread-252665.htm




- End -




看雪ID:某警官

https://bbs.pediy.com/user-home-856450.htm

  *本文由看雪论坛 某警官 原创,转载请注明来自看雪社区。



《安卓高级研修班》2021年6月班火热招生中!



# 往期推荐





公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存