Firefox在进程沙箱模型、漏洞利用缓解的研究
2021-11-17
来源:嘶吼专业版
由于Firefox母公司的隐私保护措施,Firefox 有时会被默认为一种更安全的浏览器。本文将详细反驳,并对标 Chromium,研究 Firefox 浏览器在进程沙箱模型、漏洞利用缓解技术等方面的不足。
沙箱
沙箱是一种用于隔离某些程序的技术,以防止它们中的漏洞通过限制对不必要资源的访问而危及系统的其他部分。现在所有常见的浏览器都包含一个沙箱并利用多进程架构。浏览器将自己分成不同的进程(例如内容进程、GPU 进程、RDD 进程等)并将它们分别沙箱化,严格遵守最小权限原则。浏览器使用沙箱非常重要,因为它会处理不受信任的输入,带来巨大的攻击面,并且是系统上最常用的应用程序之一。如果没有沙箱,浏览器中的任何漏洞都可以用来接管系统的其余部分。而对于沙箱,攻击者需要将他们的利用与额外的沙箱逃逸漏洞联系起来。
如果沙箱充满漏洞,那么仅仅有一个沙盒是没有多大作用的。Firefox 的沙箱非常脆弱,下面的漏洞只是几个示例。
网站隔离
网站隔离是 2018 年 Chromium 中引入的一项安全功能,这涉及对 Chromium 多进程架构的彻底改革,而不是所有的网站都运行在同一个进程中,该功能现在将每个网站划分到自己的沙箱渲染器进程中。这确保了来自一个网站的渲染器漏洞仍然无法访问来自另一个网站的数据。此外,网站隔离对于完全防止诸如 Spectre 之类的侧信道攻击是必要的。针对此类攻击的操作系统缓解措施仅保证进程边界的隔离。因此,将网站分成不同的进程是充分利用它们的唯一途径。此外,当前的浏览器缓解措施(例如降低 JavaScript 计时器的准确性)是不够的,无法解决根本问题。因此,唯一适当的缓解措施是通过网站隔离。
Firefox 目前缺乏任何形式的网站隔离,Mozilla 正在通过 Project Fission 实现这一功能,并在 Nightly 和 Beta 发布渠道上推出,但它仍在进行中,还不适合正式使用。Fission 在目前的状态下,即使它最初发布,也不会接近 Chromium 网站隔离的成熟水平,它需要很多年才能达到这一点。Fission 仍然存在基线内容流程沙箱的所有安全问题,如下所述——它不是解决所有沙箱问题的灵丹妙药。然而,具体到 Fission 本身,存在大量跨网站泄漏,允许受攻击的内容进程访问另一个的数据并绕过网站隔离。
Windows
排除网站隔离的问题,只有 Windows 上的 Firefox 沙箱可以与 Chromium 沙箱相媲美,但它仍然缺乏 Win32k Lockdown。Win32k 是 NT 内核中一组危险的系统调用,它暴露了大量的攻击面,使其成为沙箱逃逸的常见目标。Microsoft 旨在通过引入一项功能来降低这种风险,该功能允许进程阻止对这些系统调用的访问,从而大大减少攻击面。Chromium 在 2016 年实现了此功能以加强沙箱检测,但 Firefox 尚未添加此功能。
Linux
?沙箱逃逸
Firefox 在其他平台(如 Linux)上的沙箱效果要差得多,这些限制通常非常宽松,甚至容易受到跨越多年的各种琐碎的沙箱逃逸漏洞的影响,以及从沙箱内部暴露出相当大的攻击面。
此类沙箱逃逸漏洞的一个示例是 X11 — X11 没有实现任何 GUI 隔离,这使得使用它逃逸沙箱变得非常容易。Chromium 通过只允许从 GPU 进程内部访问 X11 来解决这个问题,因此渲染器进程(加载网站的进程)无法访问它,而在 Firefox 上,它直接暴露给内容进程。
PulseAudio是Linux上的一个常见的声音服务器,但是,在编写时并没有考虑到隔离,这使得使用它可以脱离沙箱。像X11一样,Firefox直接将其暴露给内容进程,允许另一个微不足道的沙箱逃脱,而Chromium只将其暴露给一个专门的音频服务。
?seccomp-bpf
seccomp-bpf 是 Linux 上的一种沙箱技术,它允许限制进程可访问的系统调用,这可以大大减少内核攻击面,并且是大多数 Linux 沙箱的核心部分。但是,Firefox 的 seccomp 过滤器比 Chromium 的沙箱强加的过滤器的限制要少得多,而且它对系统调用及其参数的限制也不相同。这方面的一个示例是对ioctl调用几乎没有过滤,只有与 TTY 相关的 ioctl 在内容过程中被阻止。这是有问题的,因为因为ioctl是一个特别强大的系统调用,它由数百个不同的系统调用组成,有点类似于NT的Win32k,因此会造成大量的内核攻击。与Firefox不同,Chromium只允许其沙箱中必需的少数 ioctl,这可显着减少内核攻击面。出于同样的原因,Android 以类似的方式在其应用程序沙箱中实现了 ioctl 过滤,以及其他各种专注于沙箱的项目。
安卓
在Android上,Firefox除了操作系统应用程序的沙箱之外,根本没有多进程架构或沙箱,而Chromium使用了isolatedProcess特性,以及更严格的seccomp-bpf过滤器。
缺少的进程
总的来说,Chromium的多进程体系结构比Firefox的更加成熟和细粒度,允许它对浏览器的每个部分施加更严格的限制。下面列出了Firefox中缺少的进程示例。在Firefox上,这些功能将被合并到另一个进程中,比如父进程或内容进程,这使得执行严格的限制变得相当困难。
在 Linux 上,Firefox 没有单独的 GPU 进程,这意味着它不能被独立沙箱化。这个进程在Windows上存在,但是它的沙箱仍然没有启用。
Firefox 还没有单独的用于网络操作的 socket 进程,这个进程只存在于 Nightly 中,不包括 WebRTC 代码以外的任何东西,这与 Chromium 的专用网络服务不同。
Firefox 也没有音频进程,而 Chromium 有专门的音频服务。Firefox 中缺少这样的进程意味着音频功能被合并到内容进程中,这就是 Linux 系统上 PulseAudio 沙箱逃逸向量的原因。
其他示例包括文本转语音、打印后端和合成器、语音识别、代理解析器等。
缓解措施方面
Firefox缺乏许多重要的缓解措施,而Chromium通常在这方面更胜一筹。
任意代码保护和代码完整性保护
一种非常常见的利用技术是,在利用缓冲区溢出漏洞期间,攻击者将他们自己的恶意代码(称为shellcode)注入到内存的一部分,并通过重写关键数据(如返回地址和函数指针),使程序执行它,劫持控制流并指向前面提到的shellcode,从而获得对程序的控制。
该行业最终演变为通过将内存的可写区域标记为不可执行和可执行区域为不可写来缓解这种类型的攻击,从而防止攻击者注入和执行其 shellcode。但是,攻击者可以通过重用程序(称为gadget)中已经存在的代码位来绕过它们,这超出了它们最初打算使用的顺序。尽管有上述保护措施,攻击者仍可以利用诸如返回导向编程 (ROP) 或跳转导向编程 (JOP) 等技术形成一系列此类gadget,以实现近乎任意的代码执行。
攻击者经常将他们的 shellcode 注入可写内存页面,然后使用这些代码重用技术将内存页面转换为可执行(使用系统调用,例如 mprotect 或 VirtualAlloc),从而允许它被执行。Windows 10实现了一个称为任意代码保护(ACG)的缓解措施,它通过确保所有可执行内存页面都是不可变的并且永远无法写入来缓解这种情况。
另一种称为代码完整性保护 (CIG) 的缓解措施类似于 ACG,但它适用于文件系统而不是内存,通过保证加载到进程中的所有二进制文件都必须签名来确保攻击者无法在磁盘上执行恶意程序或库。 ACG 和 CIG 一起在内存和文件系统中执行严格的 W^X 策略。
2017 年,Chromium 实现了对 ACG 和 CIG 的支持,如 MITIGATION_DYNAMIC_CODE_DISABLE 和 MITIGATION_FORCE_MS_SIGNED_BINS,但 Firefox 尚未实现对 ACG 或 CIG 的类似支持。目前Firefox只在socket进程中启用ACG和CIG(目前还没有启用)和在RDD进程中启用CIG。
然而,由于与JIT引擎的内在不兼容,Chromium的ACG应用目前仍然受到限制,因为JIT引擎需要同时具有可写和可执行的内存。ACG主要是在相对次要的进程中启用的,例如音频、代理解析器和图标读取器进程。如果V8启用了JITless模式,那么Chromium在渲染过程中也启用了ACG。
Chromium 在无 JITless 模式下运行时在渲染器进程中启用 ACG
如果启用了 NetworkServiceCodeIntegrity 功能,Chromium 可以选择在网络进程中启用 CIG
控制流的完整性
如前所述,代码重用攻击可用于通过将程序中已经存在的代码片段链接在一起来实现近乎任意的代码执行,ACG 和 CIG 仅缓解了一种潜在的攻击向量,创建 ROP/JOP 链以将映射转换为可执行文件。但是,攻击者仍然可以使用纯 ROP/JOP 链,完全依赖预先存在的gadget,而无需引入自己的代码。这可以通过控制流完整性 (CFI) 来缓解,CFI 严格限制攻击者能够使用的gadget,从而破坏他们的链。
CFI通常有两部分:前边缘保护(覆盖JOP、COP等)和后边缘保护(覆盖ROP)。CFI实现可以有很大的不同。有些CFI实现只覆盖前边缘或后边缘。有些是粗粒度的(攻击者有更多的通道来执行大量的指令)而不是细粒度的。有些是概率性的(它们依赖于所持有的秘密并且不保证安全属性)而不是确定性的。
Mozilla 计划实施 CFI 已有一段时间,但尚未取得太大进展。在 Linux、Android 和 ChromeOS 上,Chromium 启用 Clang 的细粒度前沿 CFI,而在 Windows 上,它启用粗粒度前沿控制流保护 (CFG)。Firefox 只在 Windows 上启用 CFG,它不如 Clang 的 CFI 有效,因为它是粗粒度的而不是细粒度的,并且不适用于其他平台。
不受信任的字体拦截
不受信任的字体历来是Windows漏洞的常见来源,因此,Windows包括一个缓解措施,以阻止来自特定进程的不受信任的字体,以减少攻击面。2016年,Chromium以MITIGATION_NONSYSTEM_FONT_DISABLE的形式添加了对这一功能的支持,并为大多数子进程启用了这一功能,但是Firefox还没有在任何子进程中启用这一功能。
JIT 强化技术
所有常见的浏览器都包含一个JIT编译器以提高性能,然而,JIT中存在一个固有的安全漏洞,那就是对可写和可执行内存的要求,这是一个W^X冲突,如上所述,可以帮助利用它。为了在不牺牲性能提升的情况下降低此功能带来的安全风险,浏览器采用了 JIT 强化技术,使利用 JIT 编译器变得更加困难。在 Chris Rohlf关于攻击 JIT 编译器的研究中,分析和比较了在各种 JIT 引擎中实现的强化技术。这项研究表明,Chromium (V8) 中的 JIT 引擎比 Firefox (JaegerMonkey) 中使用的引擎具有更好的保护。特别是,Chromium 使用但 Firefox 没有使用的缓解措施包括:
?保护页面;
?页面随机化;
?Constant blinding;
?分配限制;
?NOP 插入;
?随机代码库偏移量;
改进措施
Firefox 试图通过添加所谓的“W^X JIT”来强化 JIT 引擎,但这并不能阻止实际的漏洞利用,因为它容易受到竞争窗口的影响,在该窗口中,攻击者可以在可写时将其 shellcode 写入内存映射并等待引擎将其转换为可执行文件。此外,由于 Firefox 中缺少 CFI,攻击者也可以使用许多gadget来强制将映射转换为可执行文件,例如 C 库中的 ExecutableAllocator::makeExecutable 或 mprotect / VirtualAlloc。类似于 Safari 的“Bulletproof JIT”的东西会是一个更好的方法,它利用两个独立的映射,一个可写的和一个可执行的,可写映射被放置在内存中的一个秘密位置,通过只执行内存隐藏。
经过分析,这不是一个很成熟的安全措施。
内存分配器强化
此外,Firefox 缺乏强化的内存分配器。Firefox 目前使用 mozjemalloc,它是 jemalloc 的一个分支。Jemalloc 是一个面向性能的内存分配器,但它不关注安全性,这使得它很容易被利用。Mozjemalloc 确实为 jemalloc 添加了一些有用的安全功能,但它们不足以解决分配器整体架构中存在的问题。Chromium 改为使用 PartitionAlloc(由于 PartitionAlloc-Everywhere 在整个代码库中),它比 mozjemalloc 更加坚固。
与mozjemalloc相比,在PartitionAlloc中有一些在mozjemalloc中不存在的安全特性。
?内存分区
内存分区是一种漏洞利用缓解措施,其中内存分配器将不同类型的对象隔离到它们自己独立的堆中。
Chromium 的 PartitionAlloc 在设计时就考虑到了这种缓解措施,分区之间不重用内存的强内存分区是PartitionAlloc的核心目标之一。
Firefox的mozjemalloc最终也实现了对分区的一些支持,但是,内存是跨分区重用的,因此严重削弱了这个特性,并允许它被绕过。因此,mozjemalloc的内存分区实现并不能保证像PartitionAlloc那样的强隔离。
?脱机元数据
传统的堆利用技术通常依赖于破坏内存分配器元数据。PartitionAlloc 将大多数元数据离线存储在专用区域中(除了空闲列表指针,它们还有其他保护)而不是将其与分配相邻,从而显着增加了执行此类技术的难度。
与PartitionAlloc不同的是,mozjemalloc目前将堆元数据放在与分配一致的位置。因此,上述技术仍然是可能的,分配器元数据更容易被攻击。