此类漏洞通常是“一键式”攻击的起点,当受害者访问恶意网站时,该漏洞会危害受害者的设备。Chrome 中的渲染器 RCE 允许攻击者危害 Chrome 渲染器进程并执行任意代码。但是,渲染器进程的权限有限,因此攻击者需要将此类漏洞与第二个“沙盒逃逸”漏洞串联起来:Chrome 浏览器进程中的另一个漏洞或操作系统中的漏洞,以危害 Chrome 本身或设备。例如,Google 威胁分析小组的 Clement Lecigne 在“间谍软件供应商使用 0-day 和 n -day 攻击流行平台”中发现,由渲染器 RCE(CVE-2022-3723)、Chrome 沙盒逃逸(CVE-2022-4135)和内核错误(CVE-2022-38181)组成的链条被野外利用。
虽然许多最强大、最复杂的“一键式”攻击都具有高度针对性,而普通用户可能更容易受到网络钓鱼等不太复杂的攻击,但用户仍然应该保持 Chrome 为最新版本并启用自动更新,因为一旦补丁发布,通常可以通过分析补丁来相对快速地利用 v8 中的漏洞。
当前漏洞存在于 Chrome 中的 JIT 编译器中,该编译器根据对输入类型(例如数字类型、数组类型等)的先前了解来优化 JavaScript 函数。这称为推测优化,必须小心确保在使用优化代码时,这些对输入的假设仍然有效。JIT 引擎的复杂性在过去导致了许多安全问题,并且一直是攻击者的热门目标。Samuel Groß 撰写的 phrack 文章“利用 JavaScript JIT 引擎中的逻辑错误”很好地介绍了该主题。
Chrome 中的 JIT 编译器
Chrome 的 v8 Javascript 引擎中的 JIT 编译器称为TurboFan。Chrome 中的 Javascript 函数会根据其使用频率进行优化。首次运行 Javascript 函数时,解释器会生成字节码。随着使用不同输入反复调用该函数,会收集有关这些输入的反馈,例如它们的类型(例如,它们是整数还是对象等)。在函数运行足够次数后,TurboFan 会使用此反馈为该函数编译优化代码,并根据反馈做出假设以优化字节码。此后,将使用编译后的优化代码来执行该函数。如果在优化函数后这些假设变得不正确(例如,使用了与反馈类型不同的新输入),则将取消优化该函数,并再次使用速度较慢的字节码。读者可以查阅 Benedikt Meurer 撰写的“ V8 中的推测优化简介”,了解有关编译过程如何工作的更多详细信息。
TurboFan 本身是一个研究得很好的主题,有大量文献记录了它的内部工作原理,所以我只介绍本文所需的背景知识。Jeremy Fetiveau 撰写的文章“ TurboFan 简介”是一篇很棒的文章,涵盖了 TurboFan 的基础知识,对于理解本文的背景非常有用,尽管我也会介绍必要的材料。Samuel Groß 撰写的 phrack 文章“利用 JavaScript JIT 引擎中的逻辑错误”也涵盖了 TurboFan 和 V8 对象布局的许多相关方面。
节点和副作用
在编译优化的 JIT 代码时,TurboFan 首先访问函数中的每个字节码指令,然后将这些指令中的每一个转换成节点集合(此过程称为缩减),从而得到一种称为“节点之海”的表示形式。节点通过依赖关系相互关联,这些依赖关系在Turbolizer中表示为边,Turbolizer 是一种常用于可视化节点之海的工具。边有三种类型:控制边表示控制流图,值边表示数据流图,效果边根据节点访问对象状态的方式对节点进行排序。
例如,在以下内容中:
x.a = 0x41;
var y = x.a;
操作y = x.a
对 具有效果依赖性x.a = 0x41
,并且必须在 之后执行,x.a = 0x41
因为x.a = 0x41
会改变 的状态x
,该状态用于y = x.a
。效果边缘对于消除优化代码中的检查非常重要。
在 Chrome 中,内存布局(特别是对象中字段的偏移量)由其指定Map
,可以将其视为对象的类型信息,而Map
TurboFan 经常使用反馈中的知识来优化代码。(读者可以查阅Mathias Bynens 的“JavaScript 引擎基础:形状和内联缓存”Map
以了解更多详细信息。但就本文而言,知道确定对象字段偏移量就足够了。)
让我们更深入地看一下如何插入依赖性检查,并使用此函数作为运行示例:
function foo(obj) {
var y = obj.x;
obj.x = 1;
return y;
}
x`当访问的字段时`obj`,TurboFan 使用 参数的先前输入`obj`来推测内存布局(由 的 确定`Map`)`obj`并发出优化的代码来访问`x`。当然,在优化后调用 时可能会使用`obj`不同的,因此在函数中创建一个节点以确保在优化代码访问该字段之前具有正确的内存布局。这可以在 Turbolizer 生成的图中看到:`Map``foo``CheckMaps``obj``x
Turbolizer 图表显示 CheckMaps 在字段加载之前插入
同样,当将 存储到x
行中时obj.x = 1
,优化后的代码假定obj
具有正确的映射。但是,由于 的映射obj
在 之前进行了检查var y = obj.x
,并且这两行之间没有任何可以更改 的内容obj
,因此无需重新检查映射。实际上,TurboFan 不会在 中使用的节点CheckMaps
之前生成额外的节点:StoreField``obj.x = 1
Turbolizer 图显示,在后续存储之前省略了 CheckMaps
但是,节点有时会产生副作用,即它可能会间接改变对象。例如,调用用户定义的 JavaScript 函数的节点可能会改变任何对象:
function foo(obj) {
var y = obj.x;
callback();
obj.x = 1;
r