cve-2020-1380:IE 0day漏洞分析

  • A+

在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 的执行流:

image.png

::: hljs-center

图 1. Jscirpt9.dll 执行流

:::

在jscript9.dll 中执行JS源代码有5个主要步骤:

  1. 分析器分析JS 源码来获取抽象语法树(Abstract Syntax Tree,AST);
  2. ByteCodeGenerator 遍历AST 来生成ByteCode;
  3. Interpreter是执行ByteCode 的虚拟机。ByteCode 执行时会收集type信息这样的配置数据。
  4. 如果有代码段被调用多次,Interpreter就会发送ByteCode 和配置数据到后端的Just-In-Time (JIT) 引擎来生成机器码,然后用生成的机器码替换ByteCode 入口点。
  5. 当机器码执行时,如果有状态破坏了配置的假设,机器码就会跳出Interpreter再次执行ByteCode,以避免安全问题。

定位机器码

在介绍漏洞之前,首先要定位JIT 引擎生成的机器码。For循环常常会调用Interpreter的JIT引擎。图2是可以触发JIT 的一段JS代码:

image.png

::: hljs-center

图 2. 触发JIT的一段JS代码

:::
当循环数大于门限值(图3中代码的0x32),循环主题和内部调用函数就会发送回后端JIT引擎作业队列来生成优化的机器码:

image.png

::: hljs-center

图 3. 前端JIT触发器的代码段

:::

后端JIT引擎线程可以从队列出获取作业,并调用 jscript9!Func::Codegen来生成优化的机器码:

image.png

::: hljs-center

图 4. JIT codegen的部分代码

:::

后端JIT 引擎会通过多个步骤来生成机器码,比如构造中间表示、构建控制流图、数据流分析、分配寄存器、布局、编码等:

image.png

::: hljs-center

图 5. Func::Codegen的代码段

:::

优化的机器码生成后,可以用来替换ByteCode 循环的主体。当for循环下次被调用时,机器码就会被调用:

image.png

::: hljs-center

图 6. Loop body机器码

:::

最后,loop body机器码会调用内部的调用函数opt的机器码,如下所示:

image.png

::: hljs-center

图 7. Opt函数机器码

:::

CVE-2020-1380漏洞根源分析

CVE-2020-1380 漏洞PoC 如下图所示:

image.png

::: hljs-center

图 8. CVE-2020-1380 PoC

:::

触发该漏洞的步骤如下:

  1. For循环发送opt函数到JIT引擎。
  2. 在opt函数中,arguments operation的三行可以将value2设置为value1,然后Float32Array的第一个元素arr[0]会被设置为value1。
  3. 函数 opt在发送给JIT 引擎后,会将参数value2的值从整数0x1337 修改为callback函数的值的对象。
  4. 在函数opt的最后一次调用中,参数flag会被设置为0,基本块“if (flag == 1)” 并不会执行,该函数会将value2设置为arr[0]。因为对象会替换value2,存在类型转化,因此callback函数的值可能会在机器码中被调用。

生成的机器码直接就行了JS调用,没有检查其是否可信。Jscript9.dll用ExecuteImplicitCall 函数来确保JS的隐式调用是安全的。

首先,研究人员修改了“arguments operation”的“arguments[0] = value2”,图9是生成的JIT代码段:

image.png

::: 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会被调用:

image.png

::: hljs-center

图 10. Js::DynamicObject::ToPrimitive的代码段

:::

ExecuteImplicitCall会检查DisableImplicitFlags,如果值不等于0,JS隐式调用就不会被调用,会直接返回未定义。最后,机器码会转到Interpreter,在Interpreter 中安全地隐式调用:

image.png

::: hljs-center

图 11. ExecuteImplicitCall 的代码段

:::

当“arguments operation”被“arguments[0] = value2” 替代时,可以看到生成的机器码会在不设置DisableImplicitFlags 的情况下直接调用Js::JavascriptConversion::ToFloat_Helper :

image.png

::: hljs-center

图 12. JIT 代码段

:::

最后,隐式调用valueOf 会直接从机器码调用。攻击者可以利用no-check callback的机会来触发UAF漏洞。

image.png

::: hljs-center

图 13. 触发UAF的代码段

:::

其他触发该漏洞的方法有使用Array.prototype.splice 或 Float64Array来调用Js::JavascriptConversion::ToNumber_Helper 的会话路径。

image.png

::: hljs-center

图 14. Float64Array奔溃

:::

目前漏洞已经修复。

https://www.trendmicro.com/en_us/research/20/h/cve-2020-1380-analysis-of-recently-fixed-ie-zero-day.html

相关推荐: 如何在Node.js中逃逸vm沙箱

vm基本用法 vm模块可在V8虚拟机上下文中编译和运行nodejs代码。按照官方文档的说法,vm不是一个安全的机制,并不适合用来运行不受信任的代码。 vm的一个常见用法是做上下文隔离: ```javascript const vm = require('vm'…