内核池初探:MS10-058漏洞分析与利用

本文详细分析了Windows内核漏洞MS10-058(CVE-2010-1890),这是一个位于tcpip.sys驱动中的内核池溢出漏洞。文章从漏洞触发原理讲起,逐步讲解如何通过精心构造的IO控制请求实现池溢出,并最终实现权限提升的完整过程。

内核池初探:MS10-058漏洞分析与利用

引言

我正在研究基于池(pool)的内存破坏漏洞。因此我想为Tarjei Mandt在其首次演讲"Windows 7内核池利用"[3]中提到的漏洞编写一个PoC利用程序。我认为这是开始学习池溢出漏洞的好练习。

目录

  • 引言
  • 前言
  • 触发漏洞
  • 池喷射技术
    • 非分页对象
    • nt!PoolHitTag
  • 利用技术
    • 基本结构
    • 覆盖PoolIndex
    • 非分页池类型
    • 伪造池描述符
    • 注意事项
  • 载荷与清理
  • 致谢
  • 结论
  • 参考文献

前言

如果你想实验这个漏洞,应该阅读[1]并确保拥有易受攻击的系统。我在Windows 7 32位虚拟机(tcpip.sys 6.1.7600.16385)上测试了这个漏洞。微软关于此漏洞的公告是MS10-058,由Matthieu Suiche[2]发现,并被用作Tarjei Mandt论文[3]中的示例。

触发漏洞

tcpip!IppSortDestinationAddresses中的整数溢出导致分配了错误大小的非分页池内存块。漏洞版本与修补版本的差异如下:

1
2
3
4
5
6
7
8
9
IppSortDestinationAddresses(x,x,x)+29   imul    eax, 1Ch
IppSortDestinationAddresses(x,x,x)+2C   push    esi
IppSortDestinationAddresses(x,x,x)+2D   mov     esi, ds:__imp__ExAllocatePoolWithTag@12 
IppSortDestinationAddresses(x,x,x)+33   push    edi
IppSortDestinationAddresses(x,x,x)+34   mov     edi, 73617049h
IppSortDestinationAddresses(x,x,x)+39   push    edi   
IppSortDestinationAddresses(x,x,x)+3A   push    eax  
IppSortDestinationAddresses(x,x,x)+3B   push    ebx           
IppSortDestinationAddresses(x,x,x)+3C   call    esi ; ExAllocatePoolWithTag(x,x,x)

可以通过WSAIoctl调用(SIO_ADDRESS_LIST_SORT)触发此代码:

1
WSAIoctl(sock, SIO_ADDRESS_LIST_SORT, pwn, 0x1000, pwn, 0x1000, &cb, NULL, NULL)

池喷射技术

非分页对象

我们可以使用多种对象来操作非分页池,例如信号量对象或保留对象。由于驱动请求的是0x54字节(实际获得0x60字节块),这与I/O完成保留对象(IoCo)的大小完全匹配。

nt!PoolHitTag

要调试特定的ExFreePoolWithTag调用,可以使用池命中标签(pool hit tags)。nt!PoolHitTag会与当前释放块的池标签进行比较,如果匹配则触发调试断点。

利用技术

基本结构

池内存分为多种类型,其中两种是分页池和非分页池。池由_POOL_DESCRIPTOR结构描述,包含PoolType、ListHeads等字段。每个内存块在数据前都有一个_POOL_HEADER头,包含块大小、所属池等信息。

覆盖PoolIndex

这种攻击的基本思路是破坏池头中的PoolIndex字段。该字段用于在释放分页池块时确定它属于哪个池描述符。攻击者可以通过破坏此字段使池管理器认为特定块属于另一个池描述符。

伪造池描述符

我们需要在空地址处伪造一个池描述符。只需分配该页面并放置伪造的延迟空闲列表和ListHeads即可。当释放一个块时,如果延迟空闲列表包含至少0x20个条目,ExFreePoolWithTag将实际释放这些块并将它们放在ListHeads的适当条目上。

注意事项

值得注意的是,这种攻击在现代缓解措施下无法工作,主要原因包括:

  • PoolIndex字段的验证
  • 防止空页分配
  • Windows 8引入了NonPagedPoolNX类型
  • SMAP防止访问用户态数据
  • SMEP防止执行用户态代码

载荷与清理

经典的"写任意位置"场景目标是HalDispatchTable。我们只需用指向我们载荷(setupPayload())的指针覆盖HalDispatchTable+4。完成后,我们需要将指针恢复为hal!HaliQuerySystemInformation。

致谢

特别感谢我的朋友@0vercl0k的审查和帮助!

结论

希望你喜欢这篇文章。如果你想了解更多关于这个主题的内容,可以查看Tarjei Mandt、Zhenhua Liu和Nikita Tarakanov的最新论文。你可以在我的新github[5]上找到我的代码。

参考文献

[1] itsecdb上的漏洞详情 [2] 微软公告 [3] Windows 7内核池利用 - Tarjei Mandt的论文(必读) [4] Windows 7中的保留对象 - j00ru的优秀文章 [5] 我的MS10-058漏洞利用代码

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计