背景介绍
今天闲得没事干在github搜v8 exploit, 搜到cve-2025-10891. 给了一篇利用手法的文章, 仔细一看挺有意思的. 并不单纯因为漏洞本身, 而是看到的利用手法可能比较有代表性.
前两天是总结了另一种类型, 从TDZ入手的HoleAttack. 今天这种是从igntion bytecode错位执行入手, 可以达到shellcode执行
利用手法介绍
这种利用手法假设通过某个漏洞已经可以获取错位bytecode跳转. 而且版本不能太新, 因为用到的runtime函数(%SerializeWasmModule, %DeserializeWasmModule)在2025-08被移到了d8.wasm下, 真实浏览器环境可能用不了了. 以及在新版d8.wasm.deserializeModule有对raw machine code的hash校验, 不允许对其修改. hash校验存在bss上, 我看过了:(
错位bytecode构造
首先从错位构造入手, 因为用a = 0x12345678这样的js语句可以生成4个字节可控(实际上并不能用到0xffffffff, 由于Smi的限制)的bytecode01 0d 78 56 34 12 LdaSmi.ExtraWide [305419896], 除去两个字节用来跳转, 我们就可以有两个字节用来做别的事.
如果两个字节不够, 还可以通过a + 0x12345678来构造总计八个字节可控的bytecode01 4c 78 56 34 12 00 00 00 00 AddSmi.ExtraWide [305419896], [0]. 其中前四个字节是我们的立即数, 同样受到Smi的限制. 而后四个字节是feedback slot index, 这只能通过不停的创建新的js语句来增长.
类似于
1 | let a = 0; |
很扯淡对吧. 这样就让整个八个字节的构造相当麻烦和低效. 不过好在似乎只需要构造两次, 因为只有调用Runtime函数才需要比较长的bytecode执行, 其他很多操作都能在两个字节内完成.
wasm序列化和反序列化
主角是%SerializeWasmModule和DeserializeWasmModule. SerializeWasmModule会暴露出受到turbofan jit优化后wasm函数的裸机器码, 而%DeserializeWasmModule会将它重新包装回WasmModule.
poc如下
1 | // 随便找一个wasm函数来加载 |
在开启–allow-natives-syntax之后执行结果如下.
为啥说要有任意bytecode执行才能打呢, 因为正常浏览器环境肯定没有开--allow-natives-syntax, 所以需要我们手动构造call %DeserializeWasmModule和%SerializeWasmModule.
可能可用的cve
cve-2025-10891存在理论可能, 因为它可以达成bytecode跳转. cve-2025-9132也存在理论可能, 之前我用这个cve能打出bytecode跳转.
顺带一提, 之前我认为标准的浏览器利用链应该要是v8沙箱内部->逃逸v8沙箱->v8任意shellcode->renderer进程沙箱逃逸. 但这个利用手法似乎给了一种新的链子, 也就是不逃逸沙箱, 直接执行shellcode. 但现在是已经被修复了, 不太确定以后还会不会遇到这种不逃沙箱直接执行shellcode的链子.
修复方案, 可用范围, 完整exp
(有空再研究..先记录下…