Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析

$
0
0
【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析

2017-01-18 13:53:44
来源:srcincite.io 作者:myswsun

阅读:583次
点赞(0)
收藏





【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析

翻译:myswsun

预估稿费:130RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


0x00 前言

这个下溢是当word处理特殊的二进制文档文件时,在复制操作期间越界读取时触发,能够导致winword.exe保护模式下栈的缓冲区溢出问题。

一切听起来是戏剧性的,但是PoC触发只需要越界读取,然而本文将深入分析漏洞细节。

这个漏洞影响Microsoft Word 2007 Service Pack 3, Microsoft Office 2010 Service Pack 2 (32位版本), Microsoft Office 2010 Service Pack 2 (64位版本) 和 Microsoft Office Compatibility Pack Service Pack 3。更多的细节能从SRC-2016-0042获取。本文所有的分析是基于Microsoft Office 2010 专业版的winword.exe(v14.0.4734.1000)。

首先,来看下sample和PoC文件的不同之处。


【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析

注意到只有一个字节的不同。接下来看下哪个结构块包含了这个不同。


【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析

可以看到不同之处在于OneTableDocumentStream数据域中。Sample文件的值为0x68,而poc文件使用0xfa来触发下溢。


0x01 触发漏洞

首先,为了调试开启页堆和用户态栈跟踪:

c:\ProgramFiles\DebuggingToolsforwindows(x86)>gflags.exe-iwinword.exe+hpa+ust CurrentRegistrySettingsforwinword.exeexecutableare:02001000 ust-Createusermodestacktracedatabase hpa-Enablepageheap c:\ProgramFiles\DebuggingToolsforWindows(x86)>

然后运行poc.doc文件导致以下保护模式外异常访问:

(880.ac4):Accessviolation-codec0000005(firstchance) Firstchanceexceptionsarereportedbeforeanyexceptionhandling. Thisexceptionmaybeexpectedandhandled. eax=00000000ebx=00000000ecx=00000033edx=00000002esi=22870ffdedi=002513c4 eip=744fb40cesp=0024c694ebp=0024c69ciopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi] 0:000>kvn #ChildEBPRetAddrArgstoChild 000024c69c5e3f9b36002513bf22870ff8000000d3MSVCR90!memmove+0xfc WARNING:Stackunwindinformationnotavailable.Followingframesmaybewrong. 010024c6b05e41384322870ff8002513bf000000d3wwlib!DllGetClassObject+0x455a 020024c7445e413223002513ac002513a000004ab8wwlib!GetAllocCounters+0xcadb 03002512305e4131c6002513ac002513a000004ab8wwlib!GetAllocCounters+0xc4bb 04002512645e45f414002513ac002513a000004ab8wwlib!GetAllocCounters+0xc45e 05002512805e8da8a7002513a022872fe400000000wwlib!GetAllocCounters+0x586ac 06002512b85e89fdcb04760520002513a0ffffffffwwlib!DllGetLCID+0x2d4521 07002567f45e66e9571b1329480476009800000000wwlib!DllGetLCID+0x299a45 08002580e05e671d5b047600980025892800000001wwlib!DllGetLCID+0x685d1 09002585845e67148904760098002589281b132948wwlib!DllGetLCID+0x6b9d5 0a0025894c5e675c10047600980000249000000000wwlib!DllGetLCID+0x6b103 0b002589985e4a6ad4047600981b1329480000056ewwlib!DllGetLCID+0x6f88a 0c002589d464270be622562f100000056e00000000wwlib!GetAllocCounters+0x9fd6c 0d002589f864270ebd18bea88018bea99800258aa8MSPTLS!FsTransformBbox+0x279b3 0e00258a4c64270f2c22798de800258d4000000000MSPTLS!FsTransformBbox+0x27c8a 0f00258aec6427119600258d400000000000000000MSPTLS!FsTransformBbox+0x27cf9 1000258ca06425736a22798de8227f0ca000000000MSPTLS!FsTransformBbox+0x27f63 1100258db46428aa6f22826fd00000000000000000MSPTLS!FsTransformBbox+0xe137 1200258eac6426fbb922798de8227f0ca000000000MSPTLS!FsTransformBbox+0x4183c 13002590006425684e22798de80000000000000000MSPTLS!FsTransformBbox+0x26986

0x02 调查访问的内存

第一步我们要检查在崩溃时访问的内存。

0:000>!heap-p-a@esi address22870ffdfoundin _DPH_HEAP_ROOT@61000 inbusyallocation(DPH_HEAP_BLOCK:UserAddrUserSize-VirtAddrVirtSize) 227a13a8:22870fe019-228700002000 67be8e89verifier!AVrfDebugPageHeapAllocate+0x00000229 77126206ntdll!RtlDebugAllocateHeap+0x00000030 770ea127ntdll!RtlpAllocateHeap+0x000000c4 770b5950ntdll!RtlAllocateHeap+0x0000023a 5de2d804mso!Ordinal149+0x000074b0 5e6a754dwwlib!DllGetLCID+0x000a11c7 5e7debc2wwlib!DllGetLCID+0x001d883c 5e41f313wwlib!GetAllocCounters+0x000185ab 5e41ec32wwlib!GetAllocCounters+0x00017eca 5e41eb57wwlib!GetAllocCounters+0x00017def 5e41e72awwlib!GetAllocCounters+0x000179c2 5e423d89wwlib!GetAllocCounters+0x0001d021 5e6acca5wwlib!DllGetLCID+0x000a691f 5e422aa0wwlib!GetAllocCounters+0x0001bd38 5e43ed59wwlib!GetAllocCounters+0x00037ff1 5e43ec61wwlib!GetAllocCounters+0x00037ef9 5e48f0c3wwlib!GetAllocCounters+0x0008835b 5e48f050wwlib!GetAllocCounters+0x000882e8 5e4a6abawwlib!GetAllocCounters+0x0009fd52 64270be6MSPTLS!FsTransformBbox+0x000279b3 64270ebdMSPTLS!FsTransformBbox+0x00027c8a 64270f2cMSPTLS!FsTransformBbox+0x00027cf9 64271196MSPTLS!FsTransformBbox+0x00027f63 6425736aMSPTLS!FsTransformBbox+0x0000e137 6428aa6fMSPTLS!FsTransformBbox+0x0004183c 6426fbb9MSPTLS!FsTransformBbox+0x00026986 6425684eMSPTLS!FsTransformBbox+0x0000d61b 6426ad48MSPTLS!FsTransformBbox+0x00021b15 6428573eMSPTLS!FsTransformBbox+0x0003c50b 64285910MSPTLS!FsTransformBbox+0x0003c6dd 64285c7bMSPTLS!FsTransformBbox+0x0003ca48 6426b17aMSPTLS!FsTransformBbox+0x00021f47 0:000>!address@edi ProcessParametrs00069738inrange000690000006a000 Environment02b233d8inrange02b2300002b24000 00160000:0023d000-00023000 Type00020000MEM_PRIVATE Protect00000004PAGE_READWRITE State00001000MEM_COMMIT UsageRegionUsageStack Pid.Tid880.ac4 0:000>dd@esi 22870ffd???????????????????????????????? 2287100d???????????????????????????????? 2287101d???????????????????????????????? 2287102d???????????????????????????????? 2287103d???????????????????????????????? 2287104d???????????????????????????????? 2287105d???????????????????????????????? 2287106d???????????????????????????????? 0:000>?@ecx*4 Evaluateexpression:204=000000cc

可以看到越界读取了一个0x19字节的堆内存,试着将另外的204个字节复制到edi中。

正如结果,栈变量在顶上6个栈桢传递,下面的是通过其他变量和偏移动态计算的。没有符号直接加大了跟踪的难度。


0x03 写内存

如果我们继续从esi读,那么可以假定继续写是安全的。我知道这是一个大的猜测,但是利用ole堆喷射或者得到使用eps的的堆,是有可能控制那个偏移的数据的。但是如何覆写?我们看下目标栈地址:

0:000>!pymonado-a002513c4-s0xcc Holdon... [+]Commandused: !pymona.pydo-a002513c4-s0xcc ---------------------------------------------------- [+]Dumpingobjectat0x002513c4,0xccbytes [+]Preparingoutputfile'dumpobj.txt' -(Re)settinglogfiledumpobj.txt [+]Generatingmoduleinfotable,hangon... -Processingmodules -Done.Let'srock'nroll. >>Objectat0x002513c4(0xccbytes): OffsetAddressContentsInfo -------------------------- +000x002513c4|0x00000000 +040x002513c8|0x000bd62f +080x002513cc|0x00002001 +0c0x002513d0|0x0000ff00 +100x002513d4|0xd63b0000 +140x002513d8|0x8001000c +180x002513dc|0xff000000 +1c0x002513e0|0x0100ffff +200x002513e4|0x00000000 +240x002513e8|0x00000000 +280x002513ec|0xffffffff +2c0x002513f0|0x00000000 +300x002513f4|0x00000000 +340x002513f8|0x00000000 +380x002513fc|0x00000000 +3c0x00251400|0x00000000 +400x00251404|0xff000000 +440x00251408|0x00000000 +480x0025140c|0xff000000 +4c0x00251410|0x00000000 +500x00251414|0xff000000 +540x00251418|0x00000c48 +580x0025141c|0xffffffff +5c0x00251420|0x00000000 +600x00251424|0xff000000 +640x00251428|0x00000000 +680x0025142c|0xff000000 +6c0x00251430|0x00000000 +700x00251434|0x1b132948ptrto0x5e52ee80:wwlib!GetAllocCounters+0x128118 +740x00251438|0xff000000 +780x0025143c|0x00000000 +7c0x00251440|0x00000000 +800x00251444|0x00000000 +840x00251448|0x00000000 +880x0025144c|0x00000000 +8c0x00251450|0xff000000 +900x00251454|0x00000000 +940x00251458|0x00000000 +980x0025145c|0x00000000 +9c0x00251460|0x00000000 +a00x00251464|0x00000000 +a40x00251468|0x00000000 +a80x0025146c|0x00000000 +ac0x00251470|0x00000000 +b00x00251474|0x00000000 +b40x00251478|0x00000000 +b80x0025147c|0x00000000 +bc0x00251480|0x00000000 +c00x00251484|0x00000000 +c40x00251488|0x00000000 +c80x0025148c|0x00000000

使用mona插件,能够将拷贝剩余大小的栈地址转储,可以看见有个指针指向.text (wwlib!GetAllocCounters+0x128118)。如果没猜错,我们不应该覆写这个值。

因此,我们可能溢出了一个栈缓冲区(可能性不大)。如果我们想命中返回地址,需要知道目的地址+0x1e8处才能出现。好奇之下能够定位到这里:

... +cc0x00251490|0xff700000 +d00x00251494|0x00ffffff +d40x00251498|0x00000000 +d80x0025149c|0x00000000 ... +1dc0x002515a0|0x1b132be0 +1e00x002515a4|0x0000005e +1e40x002515a8|0x002515c4ptrtoself+0x00000200 +1e80x002515ac|0x5e415bc1wwlib!GetAllocCounters+0xee59 [+]Thismona.pyactiontook0:00:01.669000 0:000>ub0x5e415bc1 wwlib!GetAllocCounters+0xee41: 5e415ba95epopesi 5e415baa81fbffffff7fcmpebx,7FFFFFFFh 5e415bb00f873e393c00jawwlib!DllGetLCID+0x1d316e(5e7d94f4) 5e415bb68b5508movedx,dwordptr[ebp+8] 5e415bb953pushebx 5e415bba50pusheax 5e415bbb52pushedx 5e415bbce8b9e9fdffcallwwlib+0x457a(5e3f457a)

我们无法看见调用栈,因为栈向上伸展失败了:

0:000>?0x002515ac-@esp Evaluateexpression:20248=00004f18

接下来的问题是,怎么模拟继续执行?

Bannedit编写了一个很好的插件counterfeit,可以在windbg中看到用VirtualAlloc分配的块并且用标记的数据填充它。我们能继续并替换esi的值,继续复制操作。

0:000>!pycf-a2000-f _______.____ _________________/|_____________/____\____|__|/|_ _/___\/_\||\/\__\/__\___\__\/__\|\__\ \\__(<_>)|/|\|\___/||\/||\___/|||| \___>____/|____/|___|/__|\___>__||__|\___>__||__| \/\/\/\/ version1.0-bannedit Allocatedmemory@0x14130000withRWXpermissions. Fillingmemory... Finishedfillingmemory. 0:000>dd0x14130000 1413000041414141414141424141414341414144 1413001041414145414141464141414741414148 14130020414141494141414a4141414b4141414c 141300304141414d4141414e4141414f41414150 1413004041414151414141524141415341414154 1413005041414155414141564141415741414158 14130060414141594141415a4141415b4141415c 141300704141415d4141415e4141415f41414160

现在我们看到esi位于0x14130000:

0:000>g (880.ac4):Accessviolation-codec0000005(!!!secondchance!!!) eax=00000000ebx=00000000ecx=00000033edx=00000002esi=22870ffdedi=002513c4 eip=744fb40cesp=0024c694ebp=0024c69ciopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi] 0:000>r@esi=0x14130000 ... 0:000>t eax=00000000ebx=00000000ecx=00000017edx=00000002esi=14130070edi=00251434 eip=744fb40cesp=0024c694ebp=0024c69ciopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi] 0:000>dd@ediL1 002514341b132948 0:000>ddspoi(@edi)L1 1b1329485e52ee80wwlib!GetAllocCounters+0x128118 0:000>upoi(poi(@edi)) wwlib!GetAllocCounters+0x6e3b0: 5e47511855pushebp 5e4751198becmovebp,esp 5e47511b56pushesi 5e47511c8bf1movesi,ecx 5e47511ee814000000callwwlib!GetAllocCounters+0x6e3cf(5e475137) 5e475123f6450801testbyteptr[ebp+8],1 5e4751277407jewwlib!GetAllocCounters+0x6e3c8(5e475130) 5e47512956pushesi 0:000>t eax=00000000ebx=00000000ecx=00000016edx=00000002esi=14130074edi=00251438 eip=744fb40cesp=0024c694ebp=0024c69ciopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi] 0:000>ddspoi(@edi-4)L1 4141415d????????

我们能看到我们覆写数据的指针,指向了来自esi中值对应的函数。因为esi中的数据是标记过的,我们能知道用来覆写指针的偏移。

0:000>?0x5d-0x41 Evaluateexpression:28=0000001c 0:000>!pymonado-a002513c4-s0x78 Holdon... [+]Commandused: !pymona.pydo-a002513c4-s0x78 ---------------------------------------------------- [+]Dumpingobjectat0x002513c4,0x78bytes [+]Preparingoutputfile'dumpobj.txt' -(Re)settinglogfiledumpobj.txt [+]Generatingmoduleinfotable,hangon... -Processingmodules -Done.Let'srock'nroll. >>Objectat0x002513c4(0x78bytes): OffsetAddressContentsInfo -------------------------- +000x002513c4|0x41414141=ASCII'AAAA' +040x002513c8|0x41414142=ASCII'AAAB' +080x002513cc|0x41414143=ASCII'AAAC' +0c0x002513d0|0x41414144=ASCII'AAAD' +100x002513d4|0x41414145=ASCII'AAAE' +140x002513d8|0x41414146=ASCII'AAAF' +180x002513dc|0x41414147=ASCII'AAAG' +1c0x002513e0|0x41414148=ASCII'AAAH' +200x002513e4|0x41414149=ASCII'AAAI' +240x002513e8|0x4141414a=ASCII'AAAJ' +280x002513ec|0x4141414b=ASCII'AAAK' +2c0x002513f0|0x4141414c=ASCII'AAAL' +300x002513f4|0x4141414d=ASCII'AAAM' +340x002513f8|0x4141414e=ASCII'AAAN' +380x002513fc|0x4141414f=ASCII'AAAO' +3c0x00251400|0x41414150=ASCII'AAAP' +400x00251404|0x41414151=ASCII'AAAQ' +440x00251408|0x41414152=ASCII'AAAR' +480x0025140c|0x41414153=ASCII'AAAS' +4c0x00251410|0x41414154=ASCII'AAAT' +500x00251414|0x41414155=ASCII'AAAU' +540x00251418|0x41414156=ASCII'AAAV' +580x0025141c|0x41414157=ASCII'AAAW' +5c0x00251420|0x41414158=ASCII'AAAX' +600x00251424|0x41414159=ASCII'AAAY' +640x00251428|0x4141415a=ASCII'AAAZ' +680x0025142c|0x4141415b=ASCII'AAA[' +6c0x00251430|0x4141415c=ASCII'AAA\' +700x00251434|0x4141415d=ASCII'AAA]' +740x00251438|0xff000000

0x03 曝光

再次观察调用栈,注意到memmove()的调用。

0:000>kvnL2 #ChildEBPRetAddrArgstoChild 000024c69c5e3f9b36002513bf22870ff8000000d3MSVCR90!memmove+0xfc WARNING:Stackunwindinformationnotavailable.Followingframesmaybewrong. 010024c6b05e41384322870ff8002513bf000000d3wwlib!DllGetClassObject+0x455a

用Hex-Rays反编译器,可以看到这个函数只是memmove()的一个封装,并在wwlib库中调用。可以重命名sub_316d9b16函数为memmove_wrapper_1。

int__stdcallmemmove_wrapper_1(void*Src,void*Dst,size_tSize) { intresult;//eax@2 if(Size>0x7FFFFFFF) result=MSO_1511(1647603307,0); else result=(int)memmove(Dst,Src,Size); returnresult; }

如果大小大于MAX_INT,一个整形溢出异常被触发。另外也没有合理的校验小雨目的缓冲区的情况。

为了利用,我们得知道memmove()如何访问和被调用。

所以设置一个断点bp wwlib!DllGetClassObject+0x4554 ".printf \"calling memmove(%x, %x, %x);\\n\", poi(@esp), poi(@esp+4), poi(@esp+8); gc"并重新运行PoC。

callingmemmove(271164,26fb3c,e); callingmemmove(271172,26fb4a,f); callingmemmove(271148,2266efe0,3); callingmemmove(27114b,2266efe3,3); callingmemmove(27114e,2266efe6,3); callingmemmove(271151,2266efe9,3); callingmemmove(271154,2266efec,3); callingmemmove(271157,2266efef,4); callingmemmove(27115b,2266eff3,5); callingmemmove(27122e,27115b,5); callingmemmove(27115b,2266eff8,d3); (5f0.59c):Accessviolation-codec0000005(firstchance) Firstchanceexceptionsarereportedbeforeanyexceptionhandling. Thisexceptionmaybeexpectedandhandled. eax=00000000ebx=00000000ecx=00000033edx=00000002esi=2266effdedi=00271160 eip=744fb40cesp=0026c430ebp=0026c438iopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi]

有一系列的源缓冲区是0x2266efXX开头的,并且目的缓冲区是0x002711YY。怀疑这是在一个错误的循环中多次调用memmove()。

我决定分析每个调用来判断是否是独特的。在windbg中执行‘k’命令不能继续分割它,我们已经准备在上述断点减缓执行。我决定用一个轻量的windbg插件来收集返回地址:

frompykdimport* mashed=0 forframeingetStack(): mashed+=frame.returnOffset print"stackhash:0x%x"%mashed 0:000>!pysh stackhash:0x199a6804c9

现在将它添加到我们的断点,换一行并在末尾增加空格,最后重新运行:

0:010>buwwlib!DllGetClassObject+0x4554".printf\"callingmemmove(%x,%x,%x);\",poi(@esp),poi(@esp+4),poi(@esp+8);!pysh;gc" 0:010>g ... callingmemmove(190fa4,18f97c,e);stackhash:0x18a96a3a98 callingmemmove(190fb2,18f98a,f);stackhash:0x18a96a3a98 callingmemmove(190f88,49d7fe0,3);stackhash:0x1847ab6993 callingmemmove(190f8b,49d7fe3,3);stackhash:0x1847ab6993 callingmemmove(190f8e,49d7fe6,3);stackhash:0x1847ab6993 callingmemmove(190f91,49d7fe9,3);stackhash:0x1847ab6993 callingmemmove(190f94,49d7fec,3);stackhash:0x1847ab6993 callingmemmove(190f97,49d7fef,4);stackhash:0x1847ab6993 callingmemmove(190f9b,49d7ff3,5);stackhash:0x1847ab6993 callingmemmove(19106e,190f9b,5);stackhash:0x1847ad8b4c callingmemmove(190f9b,49d7ff8,d3);stackhash:0x1847ab6993 (7dc.71c):Accessviolation-codec0000005(firstchance) Firstchanceexceptionsarereportedbeforeanyexceptionhandling. Thisexceptionmaybeexpectedandhandled. eax=00000000ebx=00000000ecx=00000033edx=00000002esi=049d7ffdedi=00190fa0 eip=744fb40cesp=0018c270ebp=0018c278iopl=0nvupeiplnzacponc cs=001bss=0023ds=0023es=0023fs=003bgs=0000efl=00210212 MSVCR90!memmove+0xfc: 744fb40cf3a5repmovsdwordptres:[edi],dwordptr[esi]

现在可以判断memmove() 在一个循环中被调用,因为在同一个栈哈希值0x1847ab6993。


0x05 影响

因为不能溢出返回地址和之后在写或复制操作中会访问和用到的一些值,这个漏洞的影响非常小。

Microsoft将此漏洞修补为“Microsoft Office信息泄露漏洞”,这在本文中介绍的上下文中说的通。然而,如果我们能够在溢出覆盖栈中.text中的一个指针,这个漏洞将影响更大。

在sub_316f3232函数中,有525处调用memmove_wrapper_1(),意味着有有多种途径可以触发这个漏洞。

另外在栈中没有一个函数使用了/GS保护,意味着如果返回地址被覆盖,没有系统级别的缓解措施能够缓解它。


0x06 总结

许多复杂的漏洞在office代码中一直存在,只是难以被发现。甚至更难去调查根因并开发利用,如果微软开放了符号表,将能更好的发现漏洞。


【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析
【漏洞分析】CVE-2016-7290: 微软office word的整数下溢漏洞分析
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:http://srcincite.io/blog/2016/12/13/word-up-microsoft-word-onetabledocumentstream-underflow.html

Viewing all articles
Browse latest Browse all 12749

Trending Articles