在8月的微软补丁日,微软修复了IE 11中的一个0 day漏洞——CVE-2020-1380。该漏洞是IE JS引擎jscript9.dll
中的UAF 漏洞。
Jscirpt9.dll 执行流
Jscript9.dll作为默认的JS引擎,从IE 9开始替换了原来的jscript.dll。图1是Jscirpt9.dll 的执行流:
::: hljs-center
图 1. Jscirpt9.dll 执行流
:::
在jscript9.dll 中执行JS源代码有5个主要步骤:
- 分析器分析JS 源码来获取抽象语法树(Abstract Syntax Tree,AST);
- ByteCodeGenerator 遍历AST 来生成ByteCode;
- Interpreter是执行ByteCode 的虚拟机。ByteCode 执行时会收集type信息这样的配置数据。
- 如果有代码段被调用多次,Interpreter就会发送ByteCode 和配置数据到后端的Just-In-Time (JIT) 引擎来生成机器码,然后用生成的机器码替换ByteCode 入口点。
- 当机器码执行时,如果有状态破坏了配置的假设,机器码就会跳出Interpreter再次执行ByteCode,以避免安全问题。
定位机器码
在介绍漏洞之前,首先要定位JIT 引擎生成的机器码。For循环常常会调用Interpreter的JIT引擎。图2是可以触发JIT 的一段JS代码:
::: hljs-center
图 2. 触发JIT的一段JS代码
:::
当循环数大于门限值(图3中代码的0x32
),循环主题和内部调用函数就会发送回后端JIT引擎作业队列来生成优化的机器码:
::: hljs-center
图 3. 前端JIT触发器的代码段
:::
后端JIT引擎线程可以从队列出获取作业,并调用 jscript9!Func::Codegen
来生成优化的机器码:
::: hljs-center
图 4. JIT codegen的部分代码
:::
后端JIT 引擎会通过多个步骤来生成机器码,比如构造中间表示、构建控制流图、数据流分析、分配寄存器、布局、编码等:
::: hljs-center
图 5. Func::Codegen的代码段
:::
优化的机器码生成后,可以用来替换ByteCode 循环的主体。当for循环下次被调用时,机器码就会被调用:
::: hljs-center
图 6. Loop body机器码
:::
最后,loop body机器码会调用内部的调用函数opt的机器码,如下所示:
::: hljs-center
图 7. Opt函数机器码
:::
CVE-2020-1380漏洞根源分析
CVE-2020-1380 漏洞PoC 如下图所示:
::: hljs-center
图 8. CVE-2020-1380 PoC
:::
触发该漏洞的步骤如下:
- For循环发送opt函数到JIT引擎。
- 在opt函数中,arguments operation的三行可以将value2设置为value1,然后Float32Array的第一个元素arr[0]会被设置为value1。
- 函数 opt在发送给JIT 引擎后,会将参数value2的值从整数0x1337 修改为callback函数的值的对象。
- 在函数opt的最后一次调用中,参数flag会被设置为0,基本块“if (flag == 1)” 并不会执行,该函数会将value2设置为arr[0]。因为对象会替换value2,存在类型转化,因此callback函数的值可能会在机器码中被调用。
生成的机器码直接就行了JS调用,没有检查其是否可信。Jscript9.dll用ExecuteImplicitCall 函数来确保JS的隐式调用是安全的。
首先,研究人员修改了“arguments operation”的“arguments[0] = value2”,图9是生成的JIT代码段:
::: hljs-center
图 9. JIT代码段
:::
在类型转换函数jscript9!Js::JavascriptConversion::ToFloat_Helper 被调用前,有一些值会设置为保持在地址0x140F3F68 和 0x140F3E86中的flag。保持在0x140F3F68中的flag是ImplicitCallFlags,保存至DisableImplicitFlags中的flag是0x140F3E86。DisableImplicitFlags被设置为3 (DisableImplicitCallFlag | DisableImplicitExceptionFlag),也就是说在JS代码调用时类型转化为浮点型是不允许的。
函数Js::JavascriptConversion::ToFloat_Helper 会检查输入类型来确定要选择的类型转化路径。因为value2是一个对象,Js::DynamicObject::ToPrimitive会被调用,最后ExecuteImplicitCall会被调用:
::: hljs-center
图 10. Js::DynamicObject::ToPrimitive的代码段
:::
ExecuteImplicitCall会检查DisableImplicitFlags,如果值不等于0,JS隐式调用就不会被调用,会直接返回未定义。最后,机器码会转到Interpreter,在Interpreter 中安全地隐式调用:
::: hljs-center
图 11. ExecuteImplicitCall 的代码段
:::
当“arguments operation”被“arguments[0] = value2” 替代时,可以看到生成的机器码会在不设置DisableImplicitFlags 的情况下直接调用Js::JavascriptConversion::ToFloat_Helper :
::: hljs-center
图 12. JIT 代码段
:::
最后,隐式调用valueOf 会直接从机器码调用。攻击者可以利用no-check callback的机会来触发UAF漏洞。
::: hljs-center
图 13. 触发UAF的代码段
:::
其他触发该漏洞的方法有使用Array.prototype.splice 或 Float64Array来调用Js::JavascriptConversion::ToNumber_Helper 的会话路径。
::: hljs-center
图 14. Float64Array奔溃
:::
目前漏洞已经修复。
https://www.trendmicro.com/en_us/research/20/h/cve-2020-1380-analysis-of-recently-fixed-ie-zero-day.html
vm基本用法 vm模块可在V8虚拟机上下文中编译和运行nodejs代码。按照官方文档的说法,vm不是一个安全的机制,并不适合用来运行不受信任的代码。 vm的一个常见用法是做上下文隔离: ```javascript const vm = require('vm'…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论