跳转到主要内容
工程的博客

危机和幽灵:攻击和缓解策略

分享这篇文章

在一个早些时候博客危机和幽灵,我们分析了性能的影响在云大数据的工作量。在这篇文章中,我们解释这些事迹,他们的缓解策略,以及他们如何影响砖从安全和性能的角度来看的。

崩溃

崩溃了操作系统安全的基本假定:在用户空间中运行的应用程序不能访问内核内存。这是很重要的,因为内核内存从另一个应用程序可以包含敏感信息,如密码。执行这个访问限制,操作系统使用的页表虚拟内存划分为两个部分——一个用于内核,另一个不受信任的用户模式应用程序。内核然后取决于处理器允许更多的特权内核访问两部分同时限制用户应用程序用户部分。

事实证明,某些处理器不支持这一限制。崩溃表明,无序的执行可以泄漏内核内存到用户模式足够长的时间被一个缓存边信道攻击。一口,让我们打开这最后一句话一个例子:

  1. 在UserModeAttackBuffer无效的缓存地址。
  2. 读一个字节KernelData从内核内存变量。
  3. 读取UserModeAttackBuffer KernelData抵消的。

当由用户应用程序执行时,内核内存访问在2号线将导致段错误由于访问限制。崩溃,然而,表明一个处理器将盲目阅读内核内存之前评估任何访问权限和另外执行前第3行权限检查。这种情况由于性能优化知道无序执行。

如果一个处理器只逐行执行代码,它将不得不等到第2行,这是由于内存访问缓慢,完成之前看第3行。为了避免这种情况,处理器假设3号线最终将需要运行并行,开始执行它。而这取决于第2行完成的结果,它可以做有用的工作在等待,就像计算UserModeAttackBuffer的起始地址。

3号线已经坐在half-executed处理器,只要读取KernelData,比赛开始第2行第3行完成和实现之间的内存访问错误。原来第3行经常赢了这场比赛,尽管即将到来的断层擦除所有行处理器的结果执行无序,不删除任何缓存的效果。

边信道攻击利用这些剩余的缓存的效果。上面的例子是开放的攻击,因为3号线的看似无害的阅读UserModeAttackBuffer创建的缓存条目UserModeAttackBuffer + KernelData地址。从另一个用户模式应用程序,攻击者可以时间需要多长时间读缓冲区中的每个地址为了找出哪些地址缓存——1号线确保只有一个这样的地址。缓冲区偏移地址读最快的揭示KernelData的价值。

恶意代码已经获得了内核内存的一个字节。重复执行这个袭击表明,不同的地址内核内存是可读的500 KB / s的速度。这意味着我们认为保护敏感数据由操作系统可以在几小时内阅读。

尽管这可怕的声音,我们的客户应该松一口气。砖的安全体系结构并不取决于这个内核和用户空间隔离我们从来没有运行多个客户的工作负载在一个操作系统实例。因此不太可能目前披露的崩溃利用削弱我们的客户数据的安全。

解决危机

即使我们不认为危机是威胁砖由于我们的架构,现在有现成的修复崩溃和我们积极更新我们的系统。Linux补丁被称为内核页表隔离(KPTI)。我们在这里简要解释KPTI砖性能的影响,以及它如何可能。



图1:崩溃之前,假设用户模式代码不能访问内核数据(箭头1)敏感,但是这并不能证明是真实的(箭头2)。KPTI修复不包括内核使用的页表中的数据用户模式,防止危机袭击(箭头3)。

停止崩溃,KPTI恢复我们的基本安全假设关于内核/用户内存孤立。它给每个应用程序两套页面表而不是一个和它们之间的交换机在每个kernel-to-user user-to-kernel过渡。页表的设置在用户模式下执行不再包括内核数据的映射。这防止崩溃利用因为当恶意用户模式代码执行2号线在上面的示例中,处理器的页表不再有内核内存地址的映射。这可以防止处理器盲目阅读任何敏感的断层。

潜在的性能影响来自两个额外的页表掉期每一个系统调用,中断和异常。尽管这只相当于几个额外的指令/过渡,这些指令清除translation lookaside buffer (TLB),这可能对性能有明显的影响。TLB缓存存储虚拟地址到物理地址。当一个虚拟内存访问,处理器将请求的虚拟地址转换为物理地址和检索数据从内存。这种转换快,如果虚拟地址存储在TLB (~ 1 CPU时钟周期),但如果它不是缓慢和处理器被迫走页表找到物理地址(~ 10 - 100时钟周期)。这是一个10 - 100 x减速/ TLB小姐!

TLB是保存在系统调用,但现在所有条目的清除。这可能有明显的性能对系统调用的应用程序的影响。在大数据的背景下,我们将有可能从这些新的上下文切换,观察小减速将发生在操作,引发网络或磁盘I / O。

唉,这不是所有KPTI凄惨。新的处理器有一个进程上下文标识符(PCIDs)功能,独特的将每个TLB条目与一组页表。启用时,页表互换不清楚TLB, KPTI减少性能的影响,同时维护它所提供的安全。

幽灵

幽灵是一个类的利用,其中两个被发现,一个攻击应用程序启动一个分支预测缓存以导致受害者程序大胆执行恶意代码路径。投机执行是一种特殊类型的无序执行让幽灵类似于崩溃。恶意代码路径的执行回滚,但它留下的元数据缓存可能边信道攻击。让我们更深入的看一下这两个幽灵单独利用。

边界检查绕过

这个版本的幽灵利用数组访问的大胆尽管之前执行索引越界检查。在某种意义上,这打破了另一个基本假定由软件工程师:代码保护的条件语句只能执行如果条件是正确的。让我们来看一个例子:

  1. 如果Array1Index小于Array1Length,那么:
    1. 阅读的价值从Array1抵消Array1Index Array2Index
    2. 从Array2抵消Array2Index阅读价值

行1 a和1 b的期望是只执行如果Array1Index小于Array1Length,但是幽灵证明这并非总是如此。希望能做一些有用的工作在等待1号线检查条件,处理器猜测下一步该做什么。这个猜测是基于1号线最近的历史,如果最近一直是真的,处理器将大胆执行行1 a和1 b无序。

利用这个脆弱的代码,攻击者必须找到它在受害者的应用程序,他们可以不断地从他们的自己的应用程序调用不同Array1Index值。如果找到这样的设置,攻击者可以通过反复训练分支预测提供Array1Index值小于Array1Length受害者。这让处理器下提供指数也将减少,导致它大胆的执行线路1 a和1 b之前完成边界检查无论Array1Index提供价值。

攻击者然后调用应用程序与一个数组索引大于受害者数组长度。行1 a和1 b中发生了什么?Array1首先读取超出了界限,自由抓取数据从任何位置在受害者的地址空间。第二个使用读值影响缓存,就像崩溃的攻击。这将打开一个边信道攻击应用程序来确定是什么从受害者的内存读取通过检查缓存。

图2:攻击者多次向受害者提供行为端正的输入(箭头1)。这只访问数组(箭头2),但它影响未来行为的分支预测(箭头3)。当攻击者提供坏输入(箭头4),它使用训练行为的分支预测(箭头3)为了访问敏感数据(箭头5)。

边界检查绕过缓解

坏消息是,没有毯子修复幽灵边界检查绕过。好消息是,在实践中攻击对砖表面很小。我们每一个客户的工作负载运行在不同的虚拟机,防止恶意客户的Apache火花工作直接调用另一个客户和窃取敏感数据。

边界检查的唯一途径绕过利用可能影响客户如果恶意虚拟机直接攻击客户的虚拟机的虚拟机监控程序还在运行。当然,这要求攻击者在管理程序找到一个脆弱的代码路径。幸运的是,这样的脆弱的代码路径的完美配方范围检查后跟两个数组访问并不常见。英特尔静态分析Linux和他们只发现少量的。对于那些被发现,有一个解决方案。一个特殊的指令,LFENCE——可以插入后边界检查停止投机执行,直到检查完成。我们相信hypervisor社区正在努力确定和补丁等弱点。没有被报道。

由于这些代码路径如此罕见,对性能的影响LFENCE使用砖工作负载可能是微不足道的。

分支目标注入

这种味道的幽灵利用投机执行引发的间接分支预测器。间接分支是一种软件指令,可以跳转到多个可能的位置,功能表和虚函数是很好的例子。研究表明,攻击者可以导致这些跳跃在他或她的选择的一个位置。对于砖客户,风险在于,这可能是用于读取一个VM的私人数据从另一个VM通过攻击上运行的程序都是。幸运的是,在下一节中讨论的虚拟机监控程序补丁,缓解了这一风险。

但是在我们讨论之前,让我们来讨论利用的一个例子。这是简化,假设的来宾VM代码和程序。想象一个hypercall看起来像这样:

  1. 从客人VM GuestArgument拯救一个论点
  2. 从函数表读取的地址一行Y和跳转。

  1. 读Array1 Array2Index抵消GuestArgument并保存它
  2. 阅读Array2抵消Array2Index

和客人VM代码是这样的:

  1. 商店的地址函数表中第5行X
  2. 从函数表读取的地址一行X和跳。

  1. 跳回1号线。

一旦客人VM代码是重复运行,间接分支预测器缓存将假定它应该从第2行跳转到第5行。预测的影射,客人VM调用程序代码,传递一个GuestArgument的选择。尽管处理器忙于阅读从表函数Y在第2行,其恶意影射分支预测导致的投机执行行5和6(这些现在看起来应该很熟悉)。

等一等。分支预测器缓存从客人准备跳线2客人第5行,不是Hypervisor第5行程序第2行。不是那件事?简短的回答是否定的。分支预测有一个有限的空间,所以别名类似到相同的位置。攻击的客人只需要运行它的启动代码从一个地址共享同一个缓存槽时,虚拟机监控程序第2行。的谷歌Project Zero的博客帖子详细讨论了攻击者如何找到这样的一个地址。

把所有这一切在一起的,分支目标注入结合侧信道攻击可以阅读所有的可用内存管理程序。

图3:火车处理器的间接攻击代码分支预测器的上下文的假hypercall只需要注入的分支(箭头B),然后调用程序的实际hypercall去预期分支(箭头),而是需要注入的分支(箭头B)它可以通过边信道攻击受害者的数据的访问。

修复分支目标注入

目前有两种方法来帮助防止幽灵的间接分支目标注射。首先是一组从英特尔微码更新,使软件管理分支预测器的状态。第二个是一个编译器技术来防止间接分支分支预测的影响。

Xen hypervisor使用AWS实现了一个补丁,使用英特尔的微码更新。补丁主要是通过增加间接分支预测障碍(IBPB)所有程序入口点。这些障碍确保客人以前执行的代码不会影响接下来的分支决策的程序代码。因此,虚拟机监控程序不会大胆的可利用的代码执行。这种变化可以防止hypervisor攻击在前一节中所讨论的,保护我们客户的虚拟机从数据泄漏。

GCC编译器实现缓解LLVM和被称为返回蹦床或“retpoline”。从本质上讲,而不是使用一个间接跳转指令分支,它将分支目标地址压入堆栈,调用返回。所不同的是,返回利用一个特殊的分支预测缓存——返回堆栈缓冲区(RSB)——的状态可以很容易地修改返回之前为了取消任何恶意启动由攻击者。

这两个应该以同样的方式影响性能。在大多数情况下,他们让它好像修补代码运行没有间接分支预测,这意味着处理器必须等到真正的分支目标地址从内存中读取,直到它可以进行任何有用的工作。如果分支目标在主内存,可以高达100纳秒的暂停执行。有多少这些中断发生的数量取决于超级调用来宾vm。

接下来是什么?

危机和幽灵的发现开辟了一个全新的利用研究领域和缓解发展,安全行业才刚刚开始完全理解。虽然只有三个利用最初证明,现在这是一个非常活跃的研究领域。目前的情况很可能将继续改变在未来几周和几个月。我们将继续关注事态的发展。

如果你有兴趣的一些性能影响这些移植可能导致大规模的数据系统,阅读我们的早些时候博客

免费试着砖
看到所有工程的博客的帖子