CVE-2025-6554分析
CVE-2025-6554
Commit:609a85c2a1bd77d6f6905369f4bc4fcf34c5db09
目录:
Background Knowledge: TDZ
Background Knowledge: operator ?.
Background Knowledge: hole
Proof of concepts
Where is the hole
Background Knowledge: hole_check_bitmap
Root Cause Analysis
Patch diff Analysis
Inconsistent array range analysis
Constructing OOB
Constructing exploit primitives
Arbitrary address read/write primitives in Sandbox
Background Knowledge: TDZ
暂时性死区(TDZ)指的是:
在代码块内,使用let或const声明的变量,在声明语句之前就被访问时,JavaScript会抛出ReferenceError异常,而不是返回undefined
示例1:
1 | function test(){ |
执行逻辑:
1.JS引擎会在编译阶段发现let a;
2.它会为a创建绑定,但不会像var那样赋初值
3.进入该块作用域后,a已存在但未初始化
4.当执行到console.log(a)时,因为a还没被初始化,所以处于TDZ,进而抛出error
示例2:
1 | function test(){ |
var声明的变量会:
1.在作用域顶部变量提升
2.自动初始化为undefined
3.因此不会出现TDZ
Background Knowledge: operator ?.
示例1:
1 | let obj ={ inner: { val: 42 } }; |
执行流程:
1.计算 obj?.inner?.val
2.若在访问链中,任何 ?. 前的值为 null 或 undefined,则delete表达式立即返回 ‘true’。例如当 obj.inner 为 null 时,表达式将变为 delete undefined.val,随后返回 true。
3.否则执行常规删除逻辑
示例1等价于:
1 | if (obj == null) return true; |
注:
可选链式操作符是一种安全的访问运算符
当发生短路时,它不会抛出 TypeError 异常
删除操作仅适用于属性访问,无法删除使用 let 或 const 声明的变量
Background Knowledge: hole
‘the hole’ 是 V8 内部使用的特殊哨兵值,并非常规 JavaScript 对象。它表示变量或数组元素处于“未初始化”或“空”状态:
- 例如,在 TDZ 期间未为 ‘let’/’const’ 分配值
- 或稀疏数组中实际未存储元素的位置。
运行时访问hole将触发运行时错误或检查逻辑。
Proof of concepts:
1 | function leak_hole(){ |
若我们假设不存在漏洞来分析:
let y出现在return y之后,执行到delete时y处于临时数据区(TDZ)状态。因此会抛出ReferenceError异常,函数终止,return y未被执行,故乍看之下y似乎未被赋值。
实际情况:
然而实际情况是由于处理x?.[y].a时的漏洞,导致此时return y返回了一个hole。
用–print-bytecode参数运行JS脚本打印出来的leak_hole函数的字节码:
1.将y变量的值hole赋值到虚拟寄存器r1中
2.将x变量的值undefined赋值到r2中
3.判断x是否为undefined或者null,如果是,则跳转到第25行
4.lda 操作完以后没有经过检查就直接返回hole
Background Knowledge: hole_check_bitmap_:
hole_check_bitmap的定义点:
hole_ckeck_bitmap_ 变量为BytecodeGenerator类中用于记录一个作用域里的变量是否已经完成了hole检测。
位图中每一位表示一个变量是否已经执行过hole检查
HoleCheckElisionScope类的定义:
该函数用来管理hole_ckeck_bitmap_ 的生命周期
效果:
每次进入一个新的作用域时创建新的空bitmap
离开作用域时恢复之前的bitmap
确保hole检查状态只在当前作用域生效,防止状态泄露到别的作用域
hole_check_bitmap的典型操作流程:
当变量被第一次访问时:
1.调用 BuildThrowIfHole函数生成 ThrowReferenceErrorIfHole字节码并在生成以后调用 RememberHoleCheckInCurrentBlock 函数,将把该变量的已检查情况记录在当前作用域的bitmap中。
之后在同一作用域内再次访问该变量时:
- VariableNeedsHoleCheckInCurrentBlock返回false
- 跳过对该变量的hole检查。
Root Cause Analysis
1 | function leak_hole(){ |
在VisitDelete()中处理delete x?.[y]?.a(可选链表达式)时,未创建新的HoleCheckElisionScope;
因此,BuildThrowIfHole(y)在父级作用域的hole_check_bitmap中标记了“y已检查”
当执行到return y时,仍沿用父级的bitmap,误以为y已经经过了hole检查
return y时跳过hole检查直接返回了hole值
Patch Diff Analysis
已移除内容: 函数BuildOptionalChain()中移除了临时对象HoleCheckElisionScope elider(this);
新增内容: 在OptionalChainNullLabelScope类的构造函数初始化列表中 添加了成员 hole_check_scope_(bytecode_generator)。
Patch 总结:
VisitDelete中对于Optionchain的操作没有创建HoleCheckElisionScope类,所以patch中在OptionChainNullLabelScope的构造函数中加入了hole_check_scope( HoleCheckElisionScope 类的对象),使可选链拥有独立hole检查作用域,修复了VisitDelete中hole检查外泄的问题。
Inconsistent range analysis -> Constructing OOB
1 | function hax(trigger) { |
解释:
在训练的过程中,Feedback Vector已经被固定为了对string类型的优化路径信息
在对o.maybe_hole的length访问时,Turbofan首先会生成CheckString节点用o.maybe_hole的属性定义方式会把CheckString节点替换成TypeGuard节点,最终turbofan中的TypeGuard中的节点不会转换成TurboShaft中的任意子节点
所以对hole的访问路径直接走了对于string的优化路径
在string的路径解析下,hole.length的值是一个负数,但是编译器误以为是一个正整数。
构造数组范围分析不一致,最后编译器认为i4只有可能是100,但是实际是0
最终效果:
由于新长度值时基于错误的索引范围信息计算得出的,因此用索引0向数组写入数据。数组容量扩展逻辑被跳过,但数组长度仍然更新为101,因此获得了可用于执行越界相对读写的corrupted数组对象
为什么跳过了扩容逻辑却改变了length的大小?
这是turboshaft的某一阶段的IR状态:
上述IR状态的简化版描述:
1 | BLOCK B28: |
真实的执行流:
实际 index = 0
capacity = 8
条件 index < capacity 成立
→ 跳入 B30
→ 没有执行扩容
→ 但跳转合并到 B31,仍执行 Store *(arr+12)=101 修改了length
Constructing exploit primitives
addrof原语构造:
总结:
利用Heap FengShui,经过调试,使得corrupted[14]和victim[0]地址相同,所以用corrupted数组写corrupted[14](double形式),用
victim[0]读出就是fakeobj原语,同理即可构造addrof原语。
Arbitrary address read/write primitives in Sandbox:
1 | var arr1=[i2f(0x0004ccf5,0x000000725),i2f(0x000000725,0x00008000)]; |
用伪造对象的方式实现任意地址读写(在之前的v8 exploit level 3中有详细解释,用的是同一思路)
先运行一遍js脚本取出v8内部正确的map值(上述的0x0004ccf5),再用伪造对象的方式实现任意地址读写原语的构建
对象的内存布局示意图:
至此,可完成v8 HeapSandbox内的任意地址读写 !
Reference
- Title: CVE-2025-6554分析
- Author: zhaojunqi
- Created at : 2025-11-16 12:13:46
- Updated at : 2025-11-16 12:48:47
- Link: https://redefine.ohevan.com/2025/11/16/CVE-2025-6554分析/
- License: This work is licensed under CC BY-NC-SA 4.0.