Chromium issue 1196683、1195777分析

  • A+
所属分类:安全文章

漏洞概述


    2021年4月12日,开源浏览器内核Chromium的一处改动提交[1]引起了关注。这是Chromium Javascript引擎v8的一处漏洞bugfix。同时针对该bugfix的回归测试样本regress-1196683.js也被提交。基于该回归测试,有安全研究员公布了完整的利用代码。由于Chromium开发流程,该漏洞于4月13日Chrome的release版中才被修复[2]。


    无独有偶,4月15日Chromium代码仓库另一处改动提交[3]也包含了回归测试样本regress-1195777.js。基于该样本,完整的利用代码再次被公布。由于最新的Chrome release版没有引入该bugfix,利用代码仍可以在最新的Chrome浏览器的渲染进程稳定利用。当受影响版本的Chormium内核浏览器在没有开启沙箱的情况下(--no-sandbox),访问攻击者构造的恶意链接时,将会触发该漏洞,造成远程代码执行。


漏洞分析


Issue 1196683原理分析

     该issue的bugfix代码如下:

Chromium issue 1196683、1195777分析
    bugfix修复了v8 TurboFan在指令选择阶段针对ChangeInt32ToInt64节点的指令选择错误问题。改动前根据ChangeInt32ToInt64节点的输入节点类型选择指令。如果输入节点类型为有符号整数,则选择指令X64Movsxlq先做符号扩展,否则选择X64Movl做零扩展。Bugfix则无论输入类型,一律选择X64Movsxlq做符号扩展。根据修复代码可以猜测,这里应该是ChangeInt32ToInt64节点输入类型为无符号整数,导致指令选择阶段错误选择X64Movl指令引发的安全问题。


      首先通过regress-1196683.js分析漏洞根因:

Chromium issue 1196683、1195777分析

    触发JIT的foo函数只有一行代码,重点关注 (arr[0] ^ 0) + 1在TurboFan中关键阶段的优化过程:


  •     TyperPhase

Chromium issue 1196683、1195777分析

    异或操作符对应32节点,其两个输入分别为常量0(24节点)和arr[0](80节点)。


  •     SimplifiedLoweringPhase

Chromium issue 1196683、1195777分析

  原32节点SpeculativeNumberBitwiseXor被优化成Word32Xor,并加入后继节点ChangeInt32ToInt64。此时ChangeInt32ToInt64节点的输入节点Word32Xor类型为Signed32。


  •    EarlyOptimizationPhase

Chromium issue 1196683、1195777分析

    可以看到原32节点被删除,并用80节点替代作为110节点ChangeInt32ToInt64的输入。此时ChangeInt32ToInt64节点的输入节点LoadTypedElement类型为Unsigned32。


    这段逻辑对应的v8代码如下:

Chromium issue 1196683、1195777分析

    如上所示,对于x ^ 0 => x的情况,使用左节点替换当前节点,从而引入了错误的数据类型。


  •      InstructionSelectionPhase

    根据前面的分析,指令选择阶段,ChangeInt32ToInt64节点的输入节点LoadTypedElement类型为Unsigned32,最终选择X64Movl指令替换ChangeInt32ToInt64节点:

Chromium issue 1196683、1195777分析

  因为错误选择了零扩展指令X64Movl,导致(arr[0] ^ 0)返回错误的值:0x0000000080000000。


    最终利用该漏洞,通过如下代码可以得到一个JIT中非预期值为1的变量x(预期值应为0):

Chromium issue 1196683、1195777分析

Issue 1195777原理分析

    该issue的bugfix代码如下:

Chromium issue 1196683、1195777分析

    bugfix修复了v8 TurboFan在SimplifiedLowering阶段,对64位整数转为32位整数的数据类型转换节点生成错误的问题。改动前对于当前节点的输出类型为Signed32或Unsigned32均生成TruncateInt64ToInt32节点(截断);改动后,对于当前节点的输出类型为Unsigned32的情况,还需要检查use_info的类型,只有在use_info.type_check() == TypeCheckKind::kNone的情况下,才会生成TruncateInt64ToInt32节点。


    首先通过regress- 1195777.js分析漏洞根因:

Chromium issue 1196683、1195777分析

    触发JIT的foo函数中关键代码为return -1 < Math.max(0, x, -1)。重点关注 Math.max(0, x, -1)在TurboFan中关键阶段的优化过程:


  •     TyperPhase

Chromium issue 1196683、1195777分析

    Math.max(0, x, -1)对应了56,58节点,58节点输出作为41节点SpeculativeNumberLessThan(<)的输入。


  •     TypedLoweringPhase

Chromium issue 1196683、1195777分析

    Math.max(0, x, -1)两个常量参数0,-1(54,55节点)被替换成了常量节点32和14。


  •     SimplifiedLoweringPhase

Chromium issue 1196683、1195777分析

    原56,58的NumberMax节点,被Int64LessThan + Select节点替换;原41节点SpeculativeNumberLessThan被替换为Int32LessThan。在处理SpeculativeNumberLessThan输入节点的时候,因为输入节点(Select)的输出类型为Unsigned32,从而触发漏洞,导致76节点TruncateInt64ToInt32被错误生成。从而将Math.max(0, x, -1)的结果截断为Signed32。因此,当Math.max(0, x, -1)中的x为Unsigned32时,会被TruncateInt64ToInt32错误截断为Signed32。


    最终利用该漏洞,通过如下代码可以得到一个JIT中非预期值为1的变量x(预期值应为0):

Chromium issue 1196683、1195777分析


漏洞利用分析

    根据上面对这两个漏洞的分析可以知道:这两个漏洞是TurboFan在做整型数据类型转换(扩展、截断)时发生的错误。利用这两个漏洞,通过如下代码可以得到一个JIT中非预期值为1的变量x。接下来根据在野利用样本[4][5],分析如何根据这个错误数值为1的变量x,实现远程代码执行。


    具体利用步骤为:

  •     借助这个错误数值为1的变量x,构造一个长度为1的Array;

  •     通过Array.prototype.shift()获得一个长度为0xFFFFFFFF的越界数组;


    关键代码如下:

Chromium issue 1196683、1195777分析

    其中var arr = new Array(x);对应的JIT代码:

Chromium issue 1196683、1195777分析

    这里rdi为数组的长度,即x的值1。指针压缩后,左移一位(rdi+rdi),存放在JSArray.length属性(+0xC)。


    arr.shift();对应的JIT代码:

Chromium issue 1196683、1195777分析

    这里可以看到,arr.shift()后,数组的长度直接由常量0xFFFFFFFE赋值,其优化过程:


    (1)TyperPhase

Chromium issue 1196683、1195777分析

    这里数组长度赋值操作主要由152,153节点组成。其中152节点做Array.length-1计算操作。153节点将计算结果保存在Array.length(+0xC)处。


    (2)LoadEliminationPhase

Chromium issue 1196683、1195777分析

    这里可以看到,由于在Ignition执行过程中收集的x值为0,这里做了常量折叠(0-1=-1),从而得到常量0xFFFFFFFF。左移一位后为0xFFFFFFFE,存放在Array.length(+0xC)处。从而得到一个长度为0xFFFFFFFF的越界数组。


    得到越界数组后,后面的利用方法就比较通用了,一般为:

  •     借助这个越界数组,实现addrof/fakeobj

  •     借助addrof/fakeobj,伪造一个JSArray,实现任意地址读写


    样本中利用arr和cor实现任意地址读写的内存布局为:

Chromium issue 1196683、1195777分析

(1)首先利用漏洞获得一个长度为0xFFFFFFFF的arr(红框)

(2)利用越界的arr和cor实现addrof/fakeobj(绿框)

(3)利用越界的arr修改cor的长度(黄框)

(4)利用越界的cor,泄露cor的map和properties属性(蓝框),构造一个fake JSArray,借助这个fake JSArray实现任意地址读写


  •     借助WebAssembly执行shellcode

    最后借助WebAssembly创建一个RWX属性的内存页,拷贝shellcode至该内存页,执行shellcode。


    完整的利用演示:

Chromium issue 1196683、1195777分析


结论

    经分析,这两个v8 JIT的漏洞触发容易且利用简单,在没有开启沙箱的Chromium内核浏览器中可以稳定利用。考虑到这两个漏洞只是Chromium内核浏览器渲染进程的远程代码执行漏洞,无法穿透浏览器沙箱(默认开启),建议用户不要关闭Chromium内核浏览器的沙箱,且不要点击不明链接。


参考文献

[1]https://chromium-review.googlesource.com/c/v8/v8/+/2820971

[2]https://chromereleases.googleblog.com/2021/04/stable-channel-update-for-desktop.html

[3]https://chromium-review.googlesource.com/c/v8/v8/+/2826114

[4]https://github.com/r4j0x00/exploits/blob/master/chrome-0day/exploit.js

[5]https://github.com/avboy1337/1195777-chrome0day/blob/main/1195777.html


关于南研安全团队


深信服南研安全研究团队专注于APT攻击,在野漏洞利用追踪、研究、检测,攻防对抗等方向的研究工作。团队成员曾在Windows,MacOS/iOS,Linux/Android等主流操作系统中发现了上百个安全漏洞,在BlackHat USA、BlackHat Europe、BlackHat Asia、HITB、Microsoft BlueHat、CodeBlue、HITCON、Virus Bulletin、Pacsec、看雪SDC、Freebuf CIS等国内外知名安全会议上发表过演讲和论文。


如果你对以下技术内容感兴趣,希望和高手切磋共事,赶紧发简历过来吧(邮箱[email protected] 工作地点:南京):

APT攻击溯源】 病毒木马分析,威胁情报研究,APT溯源分析

检测引擎研发】EDR、XDR等检测引擎研发

攻防对抗技术】红蓝对抗,ATT&CK技术研究,检测规则编写

漏洞挖掘与利用】 在野漏洞利用研究,漏洞挖掘

云原生安全】 云原生安全技术研究和产品研发

MacOS安全】 macOS安全产品核心模块开发,macOS沙箱、EDP、EDR、DLP等安全产品开发

大数据安全】 研发基于大数据技术的下一代安全产品


深信服千里目安全实验室

Chromium issue 1196683、1195777分析

深信服科技旗下安全实验室,致力于网络安全攻防技术的研究和积累,深度洞察未知网络安全威胁,解读前沿安全技术。

● 扫码关注我们

本文始发于微信公众号(深信服千里目安全实验室):Chromium issue 1196683、1195777分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: