介绍去年,英国国家网络安全中心(NCSC)报告了V8编译器的一个安全漏洞,随后谷歌悄悄修复了该漏洞。漏洞ID为1003286,漏洞具体信息可点击【此处】获取。根据漏洞报告的描述,这是一个空指针解引用DoS漏洞,是一个不可利用的漏洞,只能由WASM代码触发。经过进一步分析,我们发现该漏洞还有另外一种触发方式,可以通过V8JIT编译进程进行利用。在本文中,我们将介绍利用该漏洞的技术细节,并演示如何利用该漏洞实现远程代码执行。漏洞的产生是出于代码优化的考虑。V8JIT编译器使用节点图,并通过优化流水线的几个阶段减少节点图,以生成优化的本机代码。而且这个节点图也适用于WASM编译器,可以将WASM代码编译成原生代码。节点在图中使用“Use”结构相互链接,如下所示:通过使用该结构,节点可以指定自己的输入节点和用户节点,以减少图的遍历。Use结构体中包含保存各种信息的位域,如下所示:InputIndex域表示Use结构体的输入节点的索引,可用于定位用户节点对应的输入节点。然而,这个字段只提供了17位的空间来存储索引,并且没有代码来检查这个限制是否被正确执行。因此,我们可以构建大量输入节点指向单个节点的图,从而造成整数溢出。例如0x20002表示0x2为输入节点索引。Use::input_ptr和Use::from函数都使用这个InputIndex字段来定位用户节点和相应的输入节点。这将导致节点、使用结构及其子字段之间的类型混淆。NCSC研究人员使用WebAssembly构建了一个PoC来惩罚空指针取消引用问题。WASM代码优化过程比JIT编译器简单,因此控制过程也比JIT编译器困难,因此它们无法避免空指针解引用。在分析过程中,我们发现JIT代码允许节点拥有大量输入节点:这个功能可以帮助我们构建一个不同于NCSC代码的PoC,并成功惩罚漏洞,最终实现对代码执行流程。利用尽管此漏洞存在于V8JIT编译器中,但它与其他常见的JIT编译器漏洞有很大不同。为了成功利用该漏洞,我们需要生成能够利用初始漏洞的利用代码,然后利用它们实现远程代码执行。Node、Use、Operator都是我们会因为这种类型而混淆的结构体。它们对应的结构如下:上面的Use::input_ptr和Use::from函数都是由ReplaceWithValue函数调用的,这个函数是用来替换Node值的:这里的“old_to”不是一个节点类型,而是一个Use字段:如果use->prev为空,old_to->first_use将替换为use->next。由于“old_to”本质上是Use,old_to->first_use相当于CheckMap节点的Operator。如果old_to->first_use换成use->next,那么CheckMapNode->op->opcode就会变成use->next->bit_field。此时CheckMap节点的opcode操作码会被其他opcode替换,CheckMap节点失效。这也是利用第一步的主要攻击场景:基于这个模型,我们创建了如下代码:替换JSStrictEqual节点之前的节点图正是我们想要的:现在,我们可以使PACKED_DOUBLE_ELEMENTS之间的类型和DICTIONARY_ELEMENTS数组混淆问题和访问任意读/写/addrof原语以执行远程代码。研究人员可以对Chromev77进行完整的漏洞利用测试。
