CVE-2020-14381的奇怪案例

2021-03-01 17:24:47

今天是我去年与@ bluec0re一起工作的这个有趣的内核错误的一周年纪念日,事实证明,我在其中一个锁定周末期间写了一些有关它的内容,所以我认为我会发布它。该漏洞本身是由零号项目的简·霍恩(Jann Hornof)发现的。当我接触到利用该bug所需的大多数元素时,我在这里停留在肤浅的位置,因为利用漏洞本身并没有特别令人兴奋。使我对该bug感兴趣的是它的生命周期,尤其是该补丁如何不均匀地应用于各种发行版。我还简短地谈论了硬件辅助通道,因为这是我第一次使用硬件辅助通道。

该错误已经在错误跟踪器中进行了详细描述,但这是另一个摘要。futex syscall的主要参数是用户名地址,该地址可能属于文件支持的映射。在这种情况下,futex键内核对象保留并保留对inode对象的引用,但不保留对文件的挂载点的引用。如果挂载点消失,则将释放其关联的内核结构,但是inode不会。这是一个问题,因为inode本身具有指向其中某些结构的字段,例如其super_blockstruct。

因此,futex代码路径对索引节点的进一步使用可能会触发释放后使用。 Jann在错误中强调的一个特定代码路径发生在销毁futex时:释放了对索引节点的最后一个引用,并且需要释放索引节点。这是在iput中完成的,然后将其称为iput_final。然后,iput_final及其子调用将调用存储在从super_block对象访问的super_operationsstruct中的inodemanagement函数。第一个实例发生在iput_final的开头,并调用drop_inode函数。

利用此错误需要能够:成功卸载挂载点。几年前还没做,但如今可以通过非特权用户名称空间的标准化来实现。这是一个很好的例子,该功能绝不是微不足道的安全性折衷(无特权的沙箱诉增强的内核攻击面),这反过来使所有主流发行版默认启用它们而没有太多争议,这有点让人感到惊讶。

只需一次调用即可完成所有操作,因为在iput_final的其余部分中,由于inode状态不正确,super_block损坏以及某些链接列表未链接,我们甚至还无法达到第二个super_operations函数指针调用(evict_inode)

开发想到的第一个开发途径如下:等待super_block被释放。此操作是在RCU回调中完成的,因此您需要在umount返回后等待RCU宽限期的结束,例如与膜拜。对于PoC,由于super_block平板kmalloc-2k并非超级忙,因此在加速宽限期的持续时间内喷洒分配效果很好。

将drop_inode指向一系列小工具,这些小工具将堆栈旋转到super_block或super_operations bufffer(必须在寄存器中并且几乎完全受控)。在这种情况下可以使用的常见小工具的示例是push reg; jmp / call [reg + x],然后可以与pop rsp链接; ret小工具放在[reg + x]

它依赖于对内核映像的精确了解,因此很难维护,但这与在内核空间中没有读取原语的情况下执行原始函数指针执行一样好。此类漏洞利用的可移植性本身就是SMEP的一大收获:它很少阻止漏洞利用,但使许多候选人对武器化的吸引力大大降低。

我们可以认为SMEP是理所当然的。它只有一个CPU世代,比SMAP早2年,但是没有它的情况却越来越少。另外,如果您的漏洞利用确实依赖于no-SMEP,但您的目标最终启用了SMEP软件,而您在运行时无法真正看出这一点,则您只是将privesc尝试变成了立足之地。但是,暂时没有SMAP仍然是问题。作为一个随机示例,AWS EC2 CPU名册显示一些不支持SMAP的CPU。

关于信息泄漏漏洞无论如何,要利用此漏洞,至少需要一个信息泄漏。最重要的是获取小工具的内核基础,然后我们可以使用类似堆泄漏器的功能来支持具有SMAP的CPU(在内核空间中的上述第3点中提供“攻击者控制的缓冲区”)。堆/堆栈泄漏通常也可以产生一个.text地址,因此拥有一个将用一块石头杀死两只鸟。但是,并不是每个人都准备好了正确的信息泄漏,这与常见的反KASLR论证相反。即使您确实有一个信息泄漏错误,也并不意味着它将对您当前的利用有所帮助。

例如,去年同期发布的一个很好的信息泄漏候选对象将是在核心转储中具有未初始化内存的CVE-2020-10732。但是,由于缺乏公共概念验证,因此需要了解核心转储生成代码,然后在该平板中找到一个允许我们获取.text的对象,并找到另一个对象来推断您控制的堆地址。简而言之,至少与我们正在研究的其他漏洞一样多。而且这并不意味着在一个漏洞利用中使用两个错误也意味着您需要考虑两个错误的局限性。我们正在寻找的主要错误(例如RHEL 7上没有的东西)和coredump的特权用户名称空间以及检索核心文件的能力,即不在容器中运行。幸运的是,对于我们的项目,我们已经知道我们我们以非SMAP容器为目标,因此我们能够避免将所有精力都花在一个信息泄漏漏洞上,而该漏洞最终将毫无价值。真正的漏洞利用开发人员无法提前准备功能的奢侈品。但是,如果我们以SMAP容器为目标,那将是这样,因为为此项目付出的更多努力将超出我们的资源预算。

硬件辅助通道但是,对于内核.text,情况有所不同,因为存在获取内核基础的通用的,公开记录的方法:硬件漏洞。我个人从未使用过,甚至将其视为利基开发技术,它依赖于不跨模型的不透明CPU启发式算法-弹性漏洞利用不宜考虑。我只是错了,但值得庆幸的是,我可以接触到许多了解更多的专家(@tehjh,@ _ fel1x,@ _ tsuro)。

虽然希望可以缓解允许跨安全边界泄漏内存的辅助通道,但有许多泄漏地址的辅助通道,自Spectre和朋友以来我们就没有听说过。这些辅助通道可能会保留更长的时间。在此项目中,我使用了Jump Over ASLR,该版本在Spectre于2016年之前发布。它易于理解(尤其是与上述人员的联系),并且有一些PoC正在等待根据您自己的情况进行调整(例如mario_baslrfrom @ _fel1x)。跳过ASLR依赖于分支TargetBuffer的内部工作原理,用户和内核分支可能会发生冲突。发生这种情况时,CPU有更多工作要做,可以观察到。只要您在任意触发的短内核路径中命中分支的偏移量,就可以泄漏内核基数:然后,您可以利用KASLR的低熵来尝试所有可能的基地址,并找到命中分支的基地址。

对于参数(要测量的分支),您实际上可以使用任何所需的参数。我只尝试使用导致快速返回用户态的参数的creat syscall,然后测量是否已命中sys_creat和do_sys_open偏移量。偏移量必须相当精确,但不能到字节,因为branchpredictor中似乎存在一些别名:我最初使用__fentry__作为附加分支目标,这两个符号的偏移量均为+5,即使后来我知道这些调用都可以使用动态修补。