[!Note] 关于 PS4 的背景信息:
PlayStation 4 主机采用定制的 AMD x86-64 CPU(8 核心),其 Orbis OS 基于 FreeBSD(v9.0),并包含部分 NetBSD 代码。 它还集成了大量开源软件,例如 Mono VM 和 WebKit。
PS4 使用的互联网浏览器实际上基于开源 WebKit 项目构建。 WebKit 是一个开源布局引擎,为 iOS、Wii U、3DS、PS Vita 和 PS4 的浏览器渲染网页。
PS4 的互联网浏览器实际上由两个独立的进程组成。 我们劫持以实现代码执行的是 WebKit 核心进程(负责处理 HTML 和 CSS 解析、图片解码、JavaScript 执行等)。 另一个进程负责其他所有事务:显示图形、接收手柄输入、管理历史和书签等。
PS4 WebKit 浏览器使用了多个堆分配器,分别服务于不同组件。具体如下:
CVE-2018-4386 的核心是 WebKit(v605.1.15,即 PS4 固件 6.XX 使用的版本)中 JavaScriptCore (JSC) 引擎的一个逻辑缺陷。该缺陷位于 BytecodeGenerator::hoistSloppyModeFunctionIfNecessary 函数中,涉及对非严格模式 JavaScript 中变量提升的不正确处理,特别是在 for-in 循环内部。
易受攻击的组件(ForInContext):
我们主要针对的是 ForInContext。这是 JavaScriptCore 用于管理 for-in 循环状态的内部结构,跟踪当前迭代变量以及正在枚举的属性集合。
当函数声明在 for-in 循环内被提升时,如果迭代变量被覆盖,引擎应使相关的 ForInContext 对象 失效。然而,由于该漏洞,此失效不会发生。这使得迭代变量可以被 任意对象 替换。尽管如此,引擎仍将该变量当作字符串属性名处理。
当稍后调用 op_get_direct_pname 字节码处理器时,它直接使用迭代变量作为字符串对象,而不进行类型检查。
通过传递一个精心构造的对象而非字符串,导致类型混淆,我们能够以导致内存损坏的方式利用此漏洞,甚至获得有用的利用原语,例如 addrof、fakeobj 和 任意读/写。
结构 ID: JavaScriptCore 中的每个对象,包括内部表示(如 WTF::StringImpl),都有一个 结构 ID(或类型标签),用于告知引擎该对象的类型以及如何解释其字段。
类型混淆: 我们的漏洞利用 CVE-2018-4386 漏洞,使一个 JavaScript 对象被解释为 StringImpl。这就是 create_impl() 函数的目的,它返回一个类型混淆的 WTF::StringImpl 对象,然后可以将该对象作为我们之前在 漏洞机制 部分提到的任意对象传递给 trigger() 函数。
然而,为了使其工作,内存布局和结构 ID 必须与引擎对真实字符串对象的期望“足够接近”。
JSString::toIdentifier() 内部做了什么?PS4 互联网浏览器 WebKit JavaScriptCore 引擎中有一个名为 JSString::toIdentifier() 的方法。该方法 将 JavaScript 字符串对象 (JSString) 转换为内部 Identifier 表示。
该 Identifier 在整个引擎中使用,用于高效地比较、存储和查找属性名、变量名以及其他需要被 JavaScript 引擎快速且频繁引用的字符串。
它会检查对象是否是一个有效的字符串,并且其结构 ID 是否匹配引擎对字符串对象的期望。如果字符串是一个 rope(字符串拼接),它可能会先将其展平,然后再进行转换。然后,它会为字符串检索现有的 Identifier,或者如果不存在则创建一个新的 Identifier。
该 Identifier 随后被内部用于快速的属性和变量查找。
JSString::toIdentifier() 检查的绕过方法当漏洞利用进入迭代 1024 次的 for 循环时,每次迭代都会创建一个新的类型混淆的 WTF::StringImpl 对象,该对象包含由 create_impl() 函数返回的 32 个新的 结构 ID。当这个类型混淆的对象被用作任意对象并传递给 trigger() 时,JSC 会对其调用 JSString::toIdentifier()。
JSString::toIdentifier() 会检查结构 ID 中的某些位,以确认该对象是一个有效的字符串或可以被视为字符串处理。通过生成许多具有不同布局和结构 ID 的对象,漏洞利用增加了至少有一个对象的结构 ID 能够通过 JSString::toIdentifier() 内部检查的机会。