IE漏洞分析:CVE-2020-1380

阅读量200005

|

发布时间 : 2020-08-31 10:30:28

x
译文声明

本文是翻译文章,文章原作者 trendmicro,文章来源:trendmicro.com

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

译文仅供参考,具体内容表达以及含义原文为准。

 

简介

作为8月份补丁的一部分,Microsoft修复了一个针对Internet Explorer 11的漏洞,特别是CVE-2020-1380。这是Internet Explorer的JavaScript引擎jscript9.dll中的UAF漏洞,在过去的几年中,我们观察到针对Internet Explorer的0-day攻击通常利用vbscript.dll和jscript.dll来运行shellcode。这次,目标更改为jscript9.dll,并使用现代JavaScript引擎的JIT来触发该漏洞,因此我决定深入jscrtip9.dll JIT引擎,试图找出CVE-2020-1380的具体原因。

 

Jscirpt9.dll执行管道概述

Jscript9.dll是默认的JavaScript引擎,用于替换从IE9开始的jscript.dll,下图显示了jscript9.dll的执行管道。

通常,执行jscript9.dll中的JavaScript源代码有五个主要步骤:

  • 1.解析器解析JavaScript源代码以获得抽象语法树(AST)。
  • 2.字节码生成器遍历AST并生成字节码。
  • 3.解释器是执行字节码的虚拟机,在执行字节码时收集配置文件数据,例如类型信息。
  • 4.当某些代码片段被多次调用时,比如在for循环中,解释器将字节码和配置文件数据发送到后端JIT引擎以生成机器码,然后用生成的机器码替换字节码的入口点。
  • 5.在执行机器码时,如果某些状态违反了配置文件设定,机器码就会释放到解释器,再次执行字节码,以避免任何安全问题。

 

查找机器代码

在介绍这个漏洞之前,我们首先需要定位到JIT引擎生成的机器码。for循环通常在解释器中调用JIT引擎。下图显示了一个可以触发JIT的JavaScript代码:

当循环计数大于某个阈值时(下图中的代码片段为0x32),循环体和内部调用函数opt将被发送到后端JIT引擎作业队列以生成优化的机器码:

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

后端JIT引擎通过几个步骤来生成优化的机器码,如:构建IR(intermediate representation)、内联(inlining)、构建控制流图(CFG)、数据流分析、优化、分配寄存器、布局(layout)、编码等:

当生成优化的机器码时,它将用于替换字节码循环体。当接下来调用for循环时,将改为在函数Js::InterpreterStackFrame::CallLoopBody中调用机器代码:

最后,循环体机器码将内部调用opt函数的机器码,如下所示:

 

CVE-2020-1380的漏洞原因

CVE-2020-1380的PoC如下图所示:

以下步骤可以触发该漏洞:

  • 1.for循环将opt函数发送到JIT引擎
  • 2.在opt函数中,参数操作的三行代码可以将value2设置为value1,然后将Float32Array的第一个元素arr[0]设置为value1。
  • 3.opt函数被发送到JIT引擎后,它将参数value2从整数0x1337更改为具有回调函数valueOf的对象。
  • 4.在opt函数的最后一次调用中,由于参数’flag’设置为0,所以“if (flag == 1)”没有执行,它将value2设置为arr[0]。因为一个对象替换了value2,所以会发生隐式类型转换,然后可能会在机器代码中调用回调函数valueOf。

JavaScript是一种动态语言,类型或属性可以隐式转换。生成的机器代码在没有任何检查的情况下直接进行JavaScript隐式调用是不可信的。Jscript9.dll使用函数ExecuteImplicitCall来使JavaScript隐式调用安全。

首先,我们将三行“参数操作”更改为“arguments[0] = value2”,其效果相同;下图显示了生成JIT代码片段。

在调用类型转换函数jscript9!Js::JavascriptConversion::ToFloat_Helper 之前,将一些值分别设置为存储在地址0x140F3F68和0x140F3E86中的flag。存储在0x140F3F68中的flag是ImplicitCallFlags,存储在0x140F3E86中的另一个flag是DisableImplicitFlags。DisableImplicitFlags设置为3(DisableImplicitCallFlag | DisableImplicitExceptionFlag),这表示从要调用的JavaScript代码进行隐式调用时(例如valueOf),不允许将类型转换为float。

JavascriptConversion::ToFloat_Helper函数检查输入类型,决定选择哪种类型转换。因为value2是一个对象,所以将调用Js::DynamicObject::ToPrimitive,然后最终将调用ExecuteImplicitCall:

ExecuteImplicitCall检查DisableImplicitFlags;如果该值不等于0,则不会调用JavaScript隐式调用,并直接返回undefined。最后,机器码将转交给解释器(Interpreter),并在解释器中安全地调用隐式调用:

但是,当使用三行“参数操作”替换“arguments[0] = value2”时,我们可以看到生成的机器代码直接调用Js::JavascriptConversion::ToFloat_Helper,而没有设置DisableImplicitFlags:

最后,将直接从机器代码中调用隐式调用valueOf。攻击者可以利用这个不检查回调的机会来触发UAF漏洞,例如通过Worker线程对TypedArray的ArrayBuffer内存进行过滤。

为什么这三行“参数操作”可以消除DisableImplicitFlags设置机器码引起了我的注意,我认为后端JIT GlobOpt阶段的arguments[0]的类型判断错误是根本原因。JIT引擎不知道Array.prototype的副作用,可用于更改arguments[0]的类型。在执行Array.prototype.push操作后应终止arguments[0]的类型,以避免出现这种类型判断错误的问题。

其他方法也可以触发这个漏洞,例如使用Array.prototype.splice或者使用Float64Array调用Js::JavascriptConversion::ToNumber_Helper的转换路径。这个漏洞也已经在8月补丁中修复。

 

总结

在过去的几年里,针对Internet Explorer的0-day攻击通常利用vbscrpt.dll和jscript.dll漏洞。CVE-2020-1380是特殊的,因为它针对jscript9.dll的JIT引擎。JIT漏洞是V8,JavascriptCore,Spidermonkey和Chakra等现代JavaScript引擎中的常见问题。也许攻击者现在正在选择针对Internet Explorer的JIT引擎。

本文翻译自trendmicro.com 原文链接。如若转载请注明出处。
分享到:微信
+12赞
收藏
qwert
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66