欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

内核池溢出漏洞利用实战之Windows 10篇

来源:本站整理 作者:佚名 时间:2017-08-09 TAG: 我要投稿

一、前言
本文是内核池溢出漏洞利用实战之Windows 7篇的续集,我们将会在Windows 10系统中实现相同漏洞的利用,这将更加充满挑战因为微软公司自从Windows 8后采取了大量针对内核池攻击的防御措施。本文将更加深入地分析池相关的内容,因此建议读者先阅读第一篇文章以作铺垫。
1.1Windows 8系统的防护措施
Windows 8系统在池中采取了一系列安全改善措施,在这里我不作详尽的列举,不过我们可以关注这几点:
a.真正安全的链接/断开链接
b.池索引验证:池索引覆盖攻击早已不是什么难事
c.不执行非分页池(No-Execute):这是一种新式的非分页池,可以说是非分页池的不执行(NX)版,Windows默认使用该类型的池而不是以往的非分页池
d.SMEP:管理模式执行保护
e.MIN_MAP_ADDR:内存首地址0x1000是保留地址不能被分配,这可以防御空引用类漏洞的攻击,这种防护已经在Windows 7系统和64位的Vista系统中被攻破
f. NtQuerySystemInformation()缺陷:该缺陷在低完整性场景下(通常是浏览器沙箱)不再可以被利用
关于我们利用的配额进程指针覆盖漏洞说明如下:
a.进程指针目前通过cookie进行了编码:
1).进程指针在分配块时进行如下编码:ExpPoolQuotaCookie 异或 ChunkAddress 异或 ProcessPointer
2).块空闲时进程指针被用作canary并进行如下编码:ExpPoolQuotaCookie 异或 ChunkAddress
b.进程指针被解码后必须指向内核空间否则会触发异常检测
如果你想了解详尽的Windows 8关于这方面的缓解措施你可以读一下这篇文章[1](Windows 8系统堆内部解析),另外该文作者Tarjei Mandt的另外一篇文章[2](在windows 7系统中利用内核池漏洞)提到的每一种攻击手法都已经得到了有效缓解。Windows 8系统中确实有通过控制RIP协议来获取数据的漏洞可利用,但是这些漏洞在Windows 10系统中已经通过在_OBJECT_HEADER中设置cookie的方式被修复。所以如果你想实现利用配额进程指针覆盖漏洞这种在Windows 7系统中使用过的攻击手段,我们需要做到:
1)池Cookie(PoolCookie):用它来正确编码指针
2)溢出块地址:也需要用它来编码指针
3)已知地址内核空间的任意数据:我们不仅要正确编码指针,该指针指向内核空间的同时还要指向我们伪造的一个结构。
让我们来尝试一下吧!
二、获取溢出块指针
这一部分会很简短,前提是你还记得Windows 7系统下的基本利用方式池喷射技术,好了,是时候放大招了,我们将采用高级池喷射技术,该技术在这篇文章[3](Windows内核池喷射技术)中有阐述。运用该文中的方法,我们可以预测任何可能的分配行为,当然有了IOCTL的漏洞我们很容易就能知道输入输出管理器分配给系统缓冲区(SystemBuffer)的地址,由于系统缓冲区(SystemBuffer)是溢出的,我们溢出的块在系统缓冲区(SystemBuffer)之后,因此我们可以得到块地址。注意:我之前提过几次,NtQuerySystemInformation漏洞在低完整性场景下不可利用,因此我们不能在低完整性层面拿到这个地址而是至少要在中等完整性层面。
三、获取已知地址内核空间的一些任意数据
有好几种方式可以实现这个目标,过去很长时间,我都是利用池喷射技术并结合随机IOCTL系统调用来往空闲的内核空间存放数据,但这种方式并不可靠,从那以后我找到了更加可靠的方法。
CreatePrivateNamespace函数用于在分页池中分配一个目录对象,以下是该函数的定义:
HANDLE WINAPI CreatePrivateNamespace(
  _In_opt_ LPSECURITY_ATTRIBUTES lpPrivateNamespaceAttributes,
  _In_     LPVOID                lpBoundaryDescriptor,
  _In_     LPCTSTR               lpAliasPrefix
);
吸引人眼球的地方:
1)该函数返回一个句柄,这很正常因为这只是一个对象,不过这意味着我们可以在分页池中获取该目录对象的地址。
2)该函数第二个参数是一个边界描述符,它必须唯一,所以你可以利用CreateBoundaryDescriptor函数创建它:
a.函数定义
HANDLE WINAPI CreateBoundaryDescriptor(
  _In_ LPCTSTR Name,
  _In_ ULONG   Flags
);
b.调用函数后赋值给一个变量,我们姑且起个HelloWorld!
关键点来了:边界描述符名直接存储在分页池中的对象中,因此以下代码
 

给出了分页池块:
 

《Hello World!》名存储在对象地址+0x1A8偏移处,看起来对名字没啥限制:
 

这里块大小变成了之前的两倍大,然而只是用来存储边界描述符名!顺便提一点,既然该对象的大小可控,它就变成了让分页池喷射的强大工具。不管怎样我们已经能够往内核空间存放一些任意数据了,并且还可以利用NtQuerySystemInformation漏洞获取它的地址。
四、获取池Cookie
气氛好像一下子紧张起来了。ExpPoolQuotaCookie是由驱动产生的一个指针大小的8字节Cookie(64位系统下),它的熵足够安全,我们没有办法猜测或者计算出它的值。乍看上去唯一获取池cookie的方式是发现强大但很少见的任意读取漏洞,于是我研究了ExpPoolQuotaCookie的利用过程。当在进程的配额管理过程中有池块被利用时,池类型(PoolType)会设置配额位(Quota Bit),并且有一个位于池头后8个字节(64位系统)的编码过的指针指向它:

[1] [2]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载