漏洞开发的现状,第2部分

admin 2022年10月17日10:18:33漏洞开发的现状,第2部分已关闭评论104 views字数 6007阅读20分1秒阅读模式

原文地址:https://www.crowdstrike.com/blog/state-of-exploit-development-part-2/

翻译:梦幻的彼岸

漏洞开发的现状,第2部分

在这个由两部分组成的博客系列的第1部分中,我们讨论了Windows系统上的二进制漏洞,包括在当今的网络环境中,漏洞发现者和攻击者必须应对的一些传统和当前的缓解措施。在第2部分中,我们将更详细的介绍微软已经实施的缓解措施。

当前的缓解#1:页表随机化

正如第一部分所解释的,当涉及到现代利用时,页表项(或PTE)非常重要。你可能还记得,PTEs负责执行内存的各种权限和属性。从历史上看,计算一个虚拟地址的PTE是微不足道的,因为PTE的基数在相当长的一段时间内是静态的。获得一个虚拟地址的PTE的过程是。

  • 通过除以一个页面的大小(通常为4KB),将虚拟地址转换成虚拟页面号码(VPN)。
  • 将VPN乘以PTE的大小(64位系统为8字节)。
  • 将PTE的基数加到前面操作的结果上

在编程术语中,这实质上相当于按索引的数组引用,如PteBase[VirtualPageNumber]。

在以前的Windows版本中,PTEs的基数位于静态虚拟地址ffff680`00000000。然而,在Windows 10 1607(RS1)之后,PTEs的基数被随机化了--这意味着这个过程现在不是那么微不足道了。

让计算给定虚拟地址的PTE的 "trivial"的方法之一是取消PTE基数的随机化。Windows API暴露了一个名为nt!MiGetPteAddress的函数,Morten Schenk在他2017年的BlackHat演讲中已经在之前的利用研究中使用了这个函数。

这个函数执行了与上面描述的完全相同的例程,以访问虚拟地址的PTE。然而,它在函数内部以0x13的偏移量动态地填充了PTE的基数。

漏洞开发的现状,第2部分

利用一个任意的读取基元,就有可能利用这种技术提取页表项的基数。有了PTE的基数在手,前面提到的琐碎的计算原语仍然有效。

请注意,Windows 10 1607(RS1)不仅随机化了PTE基址,还随机化了内核内存的其他14个区域的基址。虽然PTE基址是最重要的变化,但这些其他的随机化也有助于遏制某些类型的内核漏洞,这不在本文的讨论范围之内。

当前的缓解 #2: ACG

任意代码保护(ACG)是在Windows 10中引入的,是一种可选的内存损坏缓解措施,旨在阻止任意代码的执行。虽然ACG是针对微软Edge设计的,但它可以应用于大多数进程。

ROP是一种有据可查的绕过DEP的技术,最常用于返回到Windows API函数中,如VirtualProtect()。利用这个函数和用户提供的参数,攻击者和研究人员能够动态地改变内存的权限,其中恶意的shellcode驻留在RWX。使用ACG,这是不可能的。

漏洞开发的现状,第2部分

ACG防止现有的代码,如等待被变成RWX的恶意shellcode,被修改。如果一个人有一个读和一个写的基元,并且已经绕过了CFG和ASLR,ACG缓解了利用ROP通过动态操纵内存权限绕过DEP的能力。

此外,ACG防止分配新的可执行内存的能力。VirtualAlloc()是另一个流行的返回ROP的API,不能为恶意的目的分配可执行内存。本质上,内存不能动态地被改变为PAGE_EXECUTE_READWRITE。

ACG虽然是一个用户模式的缓解措施,但在内核中是通过一个名为nt!MiArbitraryCodeBlocked的Windows API函数实现的。这个函数主要是检查一个进程是否启用了ACG。

漏洞开发的现状,第2部分

进程的EPROCESS对象是内核对进程的表示,它有一个联合数据类型的成员,称为MitigationFlags,用于跟踪进程所启用的各种缓解措施。EPROCESS还包含另一个被称为MitigationFlagsValues的成员,它提供了MitigationFlags的一个人类可读的变体。

让我们检查一个启用了ACG的Edge内容进程(MicrosoftEdgeCP.exe)

漏洞开发的现状,第2部分

参照EPROCESS成员MitigationFlagsValues,我们可以看到DisableDynamicCode,也就是ACG,被设置为0x1--意味着ACG在这个进程中被启用。

漏洞开发的现状,第2部分

此时,如果为一个进程创建了动态的可执行代码,并且这个标志被设置,函数检查会返回STATUS_DYNAMIC_CODE_BLOCKED失败,导致崩溃。

此外,通过解析所有的EPROCESS对象,可以获得所有启用了ACG的运行进程的列表。

漏洞开发的现状,第2部分

虽然ACG的旁路不多,但逻辑导致研究人员和攻击者攻击JIT(即时)编译器。JavaScript是一种解释性语言--意味着它没有被编译成直接的机器代码。相反,JavaScript利用的是 "字节码"。然而,在某些情况下,JIT编译器被浏览器用来动态地将JavaScript字节码编译成实际的机器代码,以获得性能优势。这意味着在设计上,JIT编译器总是在创建动态的可执行代码。由于这种功能,ACG与JIT不兼容,在Windows 10 1703(RS2)之前,ACG在Edge内部只有有限的权力。

Alex Ionescu在Ekoparty的演讲中解释说,在1703(RS2)更新之前,由于ACG,Edge有一个线程负责JIT。由于JIT与ACG不兼容,这个 "JIT线程 "没有启用ACG--这意味着如果损害这个线程是可能的,那么就有可能规避ACG。为了解决这个问题,微软在Windows 1703(RS2)中完全为Edge JIT编译创建了一个单独的进程。为了让Edge内容进程(非JIT进程)利用JIT编译,JIT进程利用对Edge内容进程的句柄,以便在每个非JIT进程中执行JIT工作。

ACG有一个 "通用绕过",即研究人员和攻击者可以完全远离代码执行。通过利用代码重用技术,可以用ROP、JOP或COP编写整个有效载荷,这将 "遵守 "ACG的规则。与其使用代码重用技术返回到一个API中,不如直接使用它来构建整个有效载荷。此外,被破坏的浏览器将需要利用完整的代码重用沙盒逃逸。这并不理想,因为用ROP、JOP或COP编写有效载荷是非常耗时的。

ACG也被利用Edge的JIT结构绕过了。谷歌Project Zero的Ivan Fratic在Infiltrate 2018上发表了一篇演讲,解释了Edge的Content进程获得JIT进程的手柄的方式是有风险的。

一个Edge内容进程利用Windows API函数DuplicateHandle()来创建一个JIT进程可以利用的自身句柄。这方面的问题是,DuplicateHandle()函数需要一个已经建立的、具有PROCESS_DUP_HANDLE权限的目标进程的手柄。内容边缘进程利用这些权限来获得对JIT进程的句柄,并有很大的访问量,因为PROCESS_DUP_HANDLE允许一个有另一个进程的句柄的进程复制一个有最大访问量的伪句柄(例如-1)。这将允许从一个ACG被禁用的Content Edge进程中访问JIT进程。这可能导致利用内容进程对系统进行破坏,然后转向非ACG保护的JIT进程进行利用。

这些问题最终在Windows 10 RS4中被修复,显然,Edge现在使用Chromium引擎,需要注意的是,Chromium引擎也利用ACG和进程外的JIT编译器。

当前的缓解 #3: CET

由于CFG没有考虑返回边界情况,微软需要快速开发一个解决方案来保护返回地址。正如微软安全响应中心的Joe Bialek在他的OffensiveCon 2018演讲中提到的那样,微软最初通过一种基于软件的缓解措施来解决这个问题,这种缓解措施被称为RFG,即回流防护。

RFG旨在解决这个问题,利用函数序言中的额外代码,将函数的返回地址推到一个被称为 "痕迹栈 "(shadow stack)的东西上,该堆栈只包含函数合法返回指针的副本,不包含任何参数。这个影子堆栈不能从用户模式访问,因此 "受内核保护"。在一个函数的尾声,影子堆栈的返回地址副本与范围内的返回地址进行比较。如果它们不一样,就会出现崩溃。RFG虽然是一个很好的概念,但最终被微软内部的红色团队打败了,他们找到了一个通用的绕过方法,归结为在软件中实施任何影子堆栈解决方案。由于控制流劫持的任何软件实现的限制,需要一个基于硬件的解决方案。

进入英特尔CET或控制流执行技术。CET是一种基于硬件的缓解措施,它实现了一个影子堆栈,以保护堆栈上的返回地址,以及前向边缘情况,如通过间接分支跟踪(IBT)的调用/跳转。然而,根据Alex Ionescu和Yarden Shafir的说法,微软已经选择使用CFG(和XFG,这将在本文后面提到)来edge cases,而不是CET的IBT功能,其工作原理类似于Clang的CFI实现。

CET的主要讨论点是保护返回地址,本质上是阻挠ROP。CET有一个类似于RFG的方法,即使用一个痕迹栈。

当CET确定一个目标返回地址与影子堆栈上的相关保留返回地址不匹配时,就会产生一个错误。

漏洞开发的现状,第2部分

尽管作为 Intel Tiger Lake CPU系列的一部分,CET还没有进入主流消费硬件,但一些可能的绕过方法已经被构想出来。

当前的缓解 #4: XFG

Xtended Control Flow Guard,俗称XFG,是微软对CFG的 "增强 "实现。根据设计,CFG只验证存在于CFG位图中的函数--意味着在技术上,如果一个函数指针被存在于CFG位图中的另一个函数覆盖,它将是一个有效的目标。下面的图8显示[nt!HalDispatchTable+0x8],它通常指向hal!HaliQuerySystemInformation,被nt!RtlGetVersion覆盖。

漏洞开发的现状,第2部分

在执行前,kCFG位图接受RAX的值,它将是nt!RtlGetVersion而不是[nt!HalDispatchTable+0x8],以确定该函数是否有效。

漏洞开发的现状,第2部分
nt!guard_dispatch_icall

漏洞开发的现状,第2部分
进行逐位检查,并且仍然允许进行函数调用,即使[nt!HalDispatchTable+0x8]已被另一个函数覆盖。

漏洞开发的现状,第2部分

漏洞开发的现状,第2部分

尽管CFG确实阻止了一些间接的函数调用被覆盖的函数,但通过精心设计的函数调用,仍有可能进行具有恶意的调用。

XFG解决了这种缺乏鲁棒性(robustness)的问题,正如微软的David Weston所提到的。在David在BlueHat Shanghai 2019的演讲中,他解释说XFG实现了一个受保护函数的 "基于类型的哈希",它被放置在对XFG调度函数之一的调用上方0x8字节。

XFG本质上是取一个由返回值和函数参数组成的函数原型,并创建一个~55比特的原型哈希值。当调度函数被调用时,该函数的哈希值被放置在函数本身上方8个字节。这个哈希值将被用作控制流传输前的一个额外检查。

漏洞开发的现状,第2部分
如果一个由编译器生成的XFG函数哈希值不是严格和完整的,哈希值可能不是唯一的。这意味着,如果构成哈希值的字节序列不是唯一的,例如,当调用到一个函数的中间时,位于哈希值下8个字节的操作码可能包含相同的字节。虽然不太可能,但这可能导致XFG宣布一个被覆盖的函数是 "有效的",因为当分解成操作码时,哈希和函数之间的比较可能是真的--导致XFG被绕过。然而,编译器团队已经专门实现了代码,试图避免这种情况的发生。同样,由于C函数的散列使用原始类型,如void*,函数有可能被具有相同/相似原型的函数覆盖。

当前的缓解 #5: VBS 和 HVCI

为了为Windows操作系统提供额外的安全边界,Microsoft选择利用现代硬件的现有虚拟化功能。这些缓解措施包括虚拟机监控程序保护的代码完整性(HVCI)和基于虚拟化的安全性(VBS)。

VBS负责启用HVCI,并在“安全核心”系统上的Windows 10 1903(19H1)之后的兼容硬件上默认启用。对于通过系统配置选择加入的供应商,如果硬件足够现代化,符合微软的“安全级别3”基线,也可以在Windows 10 2003(20H1)系统上默认启用该功能。VBS旨在通过在Hyper-V虚拟机监控程序上运行来隔离用户模式和内核模式代码。

来自Windows Internals, Part 1, 7th Edition (Ionescu, et al.)的图片概述了VBS实现的高级可视化。

VTL,即虚拟信任级别,防止在一个VTL中运行的进程访问另一个VTL的资源。这是因为位于正常内核内的资源实际上是由一个更 "可信 "的边界--VTL 1管理的。

本博客中提到的VBS的主要组成部分之一是HVCI。HVCI本质上是内核中的ACG。HVCI阻止了在内核中动态创建的可执行代码。此外,HVCI防止分配RWX的内核池内存,类似于ACG通过VirtualAlloc()对RWX页面的用户模式保护。

HVCI利用第二层地址转换,即SLAT,强制执行增强型页表,或EPT,这是额外的不可改变的位(在VTL 0的背景下),在VTL 0页上设置VTL 1权限。这意味着,即使攻击者或研究人员能够在VTL 0的内核模式下操纵PTE控制位,VTL 1 EPT位仍不允许在VTL 0的内核模式下执行被操纵的页面。

HVCI的绕过方法可以包括类似于ACG的纯数据攻击技术。远离执行代码,而是利用不会导致PTE操作或其他禁止行为的代码重用技术,仍然是一个可行的选择。此外,如果攻击者/研究人员能够利用管理程序中的漏洞,或在VTL1中运行的安全内核中的漏洞,就有可能破坏VTL1的完整性。

总结

这两篇博客文章中的漏洞分类和缓解措施并不是一个详尽的列表。前面提到的这些缓解措施在许多Windows系统中通常都是默认启用的,从对抗或研究的角度来看,至少必须考虑到这一点。

许多攻击者通常选择“阻力最小的路径”,即向毫无戒备的目标用户列表发送恶意文档或恶意HTA。一般来说,这将足以完成这项工作。然而,与之相反的是,在SMB、RDP或DNS等公共服务中,有什么东西可以超过无用户交互、未经身份验证的远程内核代码执行利用吗?利用社会工程技术依赖于其他不可控因素,例如收到此类网络钓鱼电子邮件的具有安全意识的最终用户。二进制利用将人的因素排除在代码执行过程之外,减少了需要担心的问题。

研究人员或攻击者可能会花几周或几个月的时间来开发一个可靠的、可移植的漏洞,绕过所有适当的缓解措施。利用漏洞攻击(如浏览器利用漏洞攻击)可能需要一个用户模式任意读取零日来绕过ASLR;一个任意写入零日来绕过DEP、CFG、ACG和其他缓解措施;一个内核任意读取0-day来绕过来自受限调用者的KASLR/页表随机化,以使内核利用漏洞准备突破浏览器沙盒;以及一个内核任意写入零日来绕过内核利用漏洞攻击。这总共是四个0-day。投资回报值得吗?这些都是研究公司和攻击者必须考虑的问题。

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月17日10:18:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   漏洞开发的现状,第2部分https://cn-sec.com/archives/1353433.html