前段时间一直没有仔细研究原理, 基本就是到处抄exp加ai一把梭, 但感觉还是需要自己花时间研究学习才行, 不能太急于求成. 所以这次就花点时间研究下cve-2025-12429的原理.
这个洞属于TDZ HoleAttack的攻击面. 和之前的cve-2025-6554类似, 都是绕过v8的静态作用域分析检查
概览
作者的exp是怎么构造出越界读写的
因为Turbofan在优化的时候把js数组的越界检查去掉了为什么Turbofan会去掉数组检查
因为作者构造了特殊未被检查的变量y, 被初始化为Hole, 导致Turbofan的推断出错如何构造出未被检查的变量y?
这是这个cve的重点, 下面慢慢分析
构造原理
还没有初始化, 但可能会在初始化前被访问的变量会被v8标记为HoleCheckMode::kRequired, 并且在初始化前会被赋值为Hole这个特殊的值. 而Hole是不属于js语义的, 所以在访问的时候会有一个TDZ check.
TDZ(temporal dead zone)用来形容这个初始化之前, 但属于作用域的区域.
问题出在TDZ check的优化. v8会静态分析程序运行流, 对于确保已经被TDZ check过的变量, 就会消除重复检查.
所以就有了各种各样奇形怪状的控制流构造, 绕过v8的静态分析, 而一旦能访问到遗漏检查的变量, 就可以获取Hole的值.
v8里TDZ check重复的检查是以作用域为单位. 在调用HoleCheckElisionScope会创建一个新的HoleCheckElision作用域. 其类似于一个栈的结构, 用于在parser遍历AST语法树的时候隔离各个作用域. 而很多控制流的构造就是找到是否HoleCheckElision的作用域与实际作用域并不匹配.
cve-2025-12429这个漏洞是commit [7ce3a5517944fdac428313d80f8cd49474dce667]引入的. 这个commit修复了另一个bug, 却引入了一个新bug, 真是哭笑不得.
diff中看到, VisitInHoleCheckElisionScope被从next的作用域移到了body和next之前. 也就意味着body和next共享一个HoleCheckElision作用域.
1 | void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { |
这时候再看poc, 由于body和next共享HoleCheckElision, 按顺序执行(没有continue时), body里面的y会先执行, 只要这个y有HoleCheck就行了, 所以use(y)处的HoleCheckElision就被消除了.
1 | function use(x) { |
思考
前段时间看到一个总结很有启发, ai类似一辆开的很快的车, 但并检查从使用者期待的路线上跑偏, 需要使用者来掌舵. 而且现阶段ai对于很多问题依然不能独立解决(至少独立挖v8是不太现实), 人依然需要保持学习, 不能幻想ai一把梭解决所有问题, 让ai去研究自己也不懂的复杂问题不是很靠谱.