kavin

CVE-2020-25291:金山WPS Office远程堆溢出漏洞分析

kavin 安全防护 2022-11-25 444浏览 0

CVE-2020-25291:金山WPS Office远程堆溢出漏洞分析

0x01 漏洞描述

WPS Office是适用于Microsoft Windows,macOS,Linux,iOS和Android的办公软件,由总部位于珠海的中国软件开发商金山软件公司开发。WPS Office由三个主要组件组成:WPS Writer,WPS Presentation和WPS Spreadsheet。个人基本版本可以免费使用,WPS Office软件中存在一个远程执行代码漏洞,是当Office软件在分析特制Office文件时不正确地处理内存中的对象时引起的。成功利用此漏洞的攻击者可以在当前用户的上下文中运行任意代码。漏洞可能会导致拒绝服务,易受攻击的产品WPS Office,影响版本11.2.0.9453。

0x02 漏洞分析

在WPS Office中用于图像格式解析的Qt模块中发现存在堆溢出。嵌入WPS office的特制图像文件可能会触发此漏洞。打开特制的文档文件时,触发访问冲突。EDX指向数组的指针,而EAX是指向数组的索引。

0:000>g

(c50.b4):Accessviolation-codec0000005(firstchance)

Firstchanceexceptionsarereportedbeforeanyexceptionhandling.

Thisexceptionmaybeexpectedandhandled.

eax=000000c0ebx=006f1c48ecx=cd2aefbcedx=cd2c6f80esi=2ed7ae18edi=0000001c

eip=6ba13321esp=006f1b44ebp=006f1b44iopl=0nvupeiplnznapo;nc;

cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00210202

QtCore4!QMatrix::dy+0x48a8:

6ba133218b448210moveax,dwordptr[edx+eax*4+10h]ds:002b:cd2c7290=????????

崩溃是如何触发的?让我们看一下PNG标头格式。

00029E30FF89504E470D0A1A0A0000000D494844ÿ‰PNG........IHD

00029E405200000280000001C60403000000160AR...€...Æ.......

00029E5027FC0000000467414D410000B1889598'ü....gAMA..±ˆ•˜

00029E60F4A600000030504C5445000000800000ô¦...0PLTE...€..

00029E7000800080800000008080008000808080.€.€€...€€.€.€€€

00029E808080C0C0C0FF000000FF00FFFF000000€€ÀÀÀÿ...ÿ.ÿÿ...

00029E90FFFF00FF00FFFFFFFFFF7B1FB1C40000ÿÿ.ÿ.ÿÿÿÿÿ{.±Ä..

从偏移量0x29E31开始-0x29E34是PNG文件格式的签名标头。PNG头文件的结构:

PNGsignature-->IHDR-->gAMA-->PLTE-->pHYs-->IDAT-->IEND

在这种情况下,当WPS Office Suite中使用的QtCore库解析PLTE结构并触发堆溢出时,该漏洞位于Word文档中的嵌入式PNG文件中。在偏移量0x29E82到0x29E85处,调色板的解析失败,从而触发了堆中的内存损坏。崩溃触发之前的堆栈跟踪:

0000ee17906b8143efQtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a71

0100ee17f06b814259QtCore4!QBrush::setMatrix+0x234

0200ee58d46b8249a4QtCore4!QBrush::setMatrix+0x9e

0300ee58ec6b80cc84QtCore4!QImage::rect+0x22b

0400ee59086b857cccQtCore4!QTransform::inverted+0xec8

0500ee629c6b81c55bQtCore4!QSvgFillStyle::setFillOpacity+0x1b59

0600ee64806b896844QtCore4!QPainter::drawPixmap+0x1c98

0700ee65746d1e0fbdQtCore4!QPainter::drawImage+0x325

0800ee65946d0dd155kso!GdiDrawHoriLineIAlt+0x11a1a

在QtCore4解析嵌入式图像之前,我们可以看到来自KSO模块的最后一次调用,试图处理图像kso!GdiDrawHoriLineIAlt。使用IDA Pro分解应用程序来分析发生异常的函数。最后的崩溃路径如下(WinDBG结果):

QtCore4!QMatrix::dy+0x48a8:

6ba133218b448210moveax,dwordptr[edx+eax*4+10h]ds:002b:cd2c7290=????????

在IDA Pro中打开时,我们可以按以下方式反汇编该函数:

.text:67353315pushebp

.text:67353316movebp,esp

.text:67353318movzxeax,byteptr[ecx+edx];crashhere

.text:6735331Cmovecx,[ebp+arg_0]

.text:6735331Fmovedx,[ecx]

.text:67353321moveax,[edx+eax*4+10h]

.text:67353325movecx,eax

使用crashs转储中的信息,我们知道应用程序在0x67353321(mov eax,[edx + eax * 4 + 10h])处触发了访问冲突。我们可以看到EAX寄存器由0xc0值控制。因此,从这里我们可以根据导致异常的指令对寄存器的状态进行一些假设。需要注意的重要一点是,在发生异常之前,我们可以看到ECX(0xc0)中包含的值被写入到以下指令所定义的任意位置:

movecx,[ebp+arg_0]

此外,我们注意到,在我们的故障指令之外,EBP的偏移量存储在ECX寄存器中。我们在前面提到的指令(偏移量为0x6ba1331c)上设置了一个断点,以观察内存。断点触发后,我们可以看到第一个值c45adfbc指向另一个指针,该指针应该是指向数组的指针。

Breakpoint0hit

eax=0000000febx=004f1b40ecx=d3544100edx=0000001cesi=d1200e18edi=0000001c

eip=6ba1331cesp=004f1a34ebp=004f1a34iopl=0nvupeiplnznapo;nc;

cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00200202

QtCore4!QMatrix::dy+0x48a3:

6ba1331c8b4d08movecx,dwordptr[ebp+8]ss:002b:004f1a3c=c45adfbc

0:000>dcebp+8

004f1a3cc45adfbc00000048000000006f13830f..Z.H..........o

004f1a4c004f5cc8000000000000000000000000.\O.............

004f1a5c00000000004f65a0004f662c00000000.....eO.,fO.....

004f1a6c779eae8e00000000000000013f800000...w...........?

004f1a7c3f8000003f31e4f83f8000003f800000...?..1?...?...?

004f1a8c3f8000003f31e4f83f8000003de38800...?..1?...?...=

004f1a9c3de388003d9e1c8a3c834080004f3c00...=...=.@.<.

004f1aac4101c71c6ba133153f8000004081c71c...A.3.k...?...@

从c45adfbc观察内存引用,发现另一个指针。第一个值ab69cf80始终表示为指向它所引用的任何地方的指针。指针ab69cf80基本上是我们指针的索引数组。

0:000>dcc45adfbc

c45adfbcab69cf80d35441000000000300000280..i..AT.........

c45adfcc0000055a00000012c0c0c0c01c3870e2Z............p8.

c45adfdc40ad870e1c3870e240ad870e00000000...@.p8....@....

c45adfec00000000c0c0c0c16c1d12c000000000...........l....

c45adffcc0c0c0c0????????????????????????....????????????

c45ae00c????????????????????????????????????????????????

c45ae01c????????????????????????????????????????????????

c45ae02c????????????????????????????????????????????????

0:000>dcab69cf80

ab69cf80000000010000001c0000001000000001................//0000001cisoverwrittenintheregisterEDXandEDIbeforewetriggercrash

ab69cf90ff000000ff800000ff008000ff808000................

ab69cfa0ff000080ff800080ff008080ff808080................

ab69cfb0ffc0c0c0ffff0000ff00ff00ffffff00................//ffc0c0c0whereitwillbestoredinEAXaftercrash,atthemomentitonlytakes0xfvalueinEAX

ab69cfc0ff0000ffffff00ffff00ffffffffffff................

ab69cfd0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

ab69cfe0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

ab69cff0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

因为我们知道崩溃的路径,所以可以使用下面的命令简单地设置一个断点。该命令将获得指针值“ edx + eax * 4 + 10”,并检查其是否满足0xc0。

bp6ba13321".if(poi(edx+eax*4+10)==0xc0){}.else{gc}"

0:000>g

eax=000000c0ebx=004f1b40ecx=c45adfbcedx=ab69cf80esi=d1200e18edi=0000001c

eip=6ba13321esp=004f1a34ebp=004f1a34iopl=0nvupeiplnznapo;nc;

cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00200202

QtCore4!QMatrix::dy+0x48a8:

6ba133218b448210moveax,dwordptr[edx+eax*4+10h]ds:002b:ab69d290=????????

观察堆栈,可以看到以下执行:

004f1a386ba3cb98QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74

004f1a3cc45adfbc

004f1a4000000048

004f1a4400000000

004f1a486f13830fverifier!DphCommitMemoryForPageHeap+0x16f

004f1a4c004f5cc8

004f1a5000000000

004f1a5400000000

004f1a5800000000

004f1a5c00000000

004f1a60004f65a0

004f1a64004f662c

004f1a6800000000

004f1a6c779eae8entdll!RtlAllocateHeap+0x3e

如果我们反汇编6ba3cb98,则可以看到以下反汇编代码,真正的漏洞根本原因在于此代码。

6ba3cb898b96b4000000movedx,dwordptr[esi+0B4h]

6ba3cb8f8b4df4movecx,dwordptr[ebp-0Ch]

6ba3cb9252pushedx

6ba3cb938bd7movedx,edi

6ba3cb95ff5580calldwordptr[ebp-80h]

6ba3cb988b4e7cmovecx,dwordptr[esi+7Ch]

Cpseudocode

grad=*(&ptr_grad);

if(grad>0.0099999998)

{

input_value=grad_size(check,size,input);

ptr_grad=*(input);

...cuthere...

我们在6ba3cb89地址上设置断点并观察ESI + 0xB4,我们可以看到一个指针指向另一个位置:

0:000>r

eax=00000000ebx=00791878ecx=00000005edx=00793938esi=cb07de18edi=0000001c

eip=6ba3cb89esp=00791780ebp=00791870iopl=0nvupeiplnznapo;nc;

cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00200202

QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a65:

6ba3cb898b96b4000000movedx,dwordptr[esi+0B4h]ds:002b:cb07decc=cf69afbc

0:000>dcesi+0B4h

cb07decccf69afbcc0c0c0000000000000000100..i.............

cb07dedcc0c0c0c0000000000000000000000000................

cb07deec00000000000000000000000000000000................

cb07defc00000000cf030fd00000000000000000................

cb07df0c00000000000000000000000000000000................

cb07df1cc0c0c0c0000000003ff0000000000000...........?....

cb07df2c00000000000000000000000000000000................

cb07df3c00000000000000003ff0000000000000...........?....

0:000>dccf69afbc

cf69afbcc88baf80d13261000000000300000280.....a2.........

cf69afcc0000055f00000012c0c0c0c01c3870e2_............p8.

cf69afdc40ad870e1c3870e240ad870e00000000...@.p8....@....

cf69afec00000000c0c0c0c16c1d12c000000000...........l....

cf69affcc0c0c0c0????????????????????????....????????????

cf69b00c????????????????????????????????????????????????

cf69b01c????????????????????????????????????????????????

cf69b02c????????????????????????????????????????????????

0:000>dcc88baf80

c88baf80000000010000001c0000001000000001................

c88baf90ff000000ff800000ff008000ff808000................

c88bafa0ff000080ff800080ff008080ff808080................

c88bafb0ffc0c0c0ffff0000ff00ff00ffffff00................

c88bafc0ff0000ffffff00ffff00ffffffffffff................

c88bafd0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

c88bafe0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

c88baff0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

从这里我们可以知道代码实际上没有从指针释放任何东西。一旦移至EDX,EDX将保留指向索引数组的指针:

eax=00000000ebx=00791878ecx=00000005edx=cf69afbcesi=cb07de18edi=0000001c

eip=6ba3cb8fesp=00791780ebp=00791870iopl=0nvupeiplnznapo;nc;

cs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00200202

QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a6b:

6ba3cb8f8b4df4movecx,dwordptr[ebp-0Ch]ss:002b:00791864=d1326100

0:000>dccf69afbc

cf69afbcc88baf80d13261000000000300000280.....a2.........

cf69afcc0000055f00000012c0c0c0c01c3870e2_............p8.

cf69afdc40ad870e1c3870e240ad870e00000000...@.p8....@....

cf69afec00000000c0c0c0c16c1d12c000000000...........l....

cf69affcc0c0c0c0????????????????????????....????????????

cf69b00c????????????????????????????????????????????????

cf69b01c????????????????????????????????????????????????

cf69b02c????????????????????????????????????????????????

0:000>dcc88baf80

c88baf80000000010000001c0000001000000001................

c88baf90ff000000ff800000ff008000ff808000................

c88bafa0ff000080ff800080ff008080ff808080................

c88bafb0ffc0c0c0ffff0000ff00ff00ffffff00................

c88bafc0ff0000ffffff00ffff00ffffffffffff................

c88bafd0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

c88bafe0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

c88baff0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0................

崩溃后的堆栈跟踪:

0:000>kvL

#ChildEBPRetAddrArgstoChild

00012f18d46ba3cb98cc53afbc0000004800000000QtCore4!QMatrix::dy+0x48a8

01012f19d06b8143ef00000000012f1b78012f1a5cQtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74

02012f1a306b8142590000002e012f5bd000000000QtCore4!QBrush::setMatrix+0x234

03012f5b146b8249a40000003b012f5b68cc780e18QtCore4!QBrush::setMatrix+0x9e

04012f5b2c6b80cc840000003b012f5b68cc780e18QtCore4!QImage::rect+0x22b

05012f5b486b857ccc0000003b012f5b68cc780e18QtCore4!QTransform::inverted+0xec8

06012f64dc6b81c55b00000000003c000000000000QtCore4!QSvgFillStyle::setFillOpacity+0x1b59

07012f66c06b896844012f6724cc818ff00000001cQtCore4!QPainter::drawPixmap+0x1c98

08012f67b46d1e0fbd012f69ec012f66d4012f6864QtCore4!QPainter::drawImage+0x325

09012f67d46d0dd155012f6a54012f69ec012f6864kso!GdiDrawHoriLineIAlt+0x11a1a

0a012f67ec6d0c8d88012f69ec012f68e0012f6864kso!kpt::PainterExt::drawBitmap+0x23

堆分析:

0:000>!heap-p-acc53afbc

addresscc53afbcfoundin

_DPH_HEAP_ROOT@6731000

inbusyallocation(DPH_HEAP_BLOCK:UserAddrUserSize-VirtAddrVirtSize)

cc36323c:cc53afa858-cc53a0002000

6f13ab70verifier!AVrfDebugPageHeapAllocate+0x00000240

77a9909bntdll!RtlDebugAllocateHeap+0x00000039

779ebbadntdll!RtlpAllocateHeap+0x000000ed

779eb0cfntdll!RtlpAllocateHeapInternal+0x0000022f

779eae8entdll!RtlAllocateHeap+0x0000003e

6f080269MSVCR100!malloc+0x0000004b

6f08233bMSVCR100!operatornew+0x0000001f

6b726c67QtCore4!QImageData::create+0x000000fa

6b726b54QtCore4!QImage::QImage+0x0000004e

6b7a0e21QtCore4!png_get_text+0x00000436

6b79d7a8QtCore4!QImageIOHandler::setFormat+0x000000de

6b79d457QtCore4!QPixmapData::fromFile+0x000002bf

6b725eb4QtCore4!QImageReader::read+0x000001e2

6d0ca585kso!kpt::VariantImage::forceUpdateCacheImage+0x0000254e

6d0c5964kso!kpt::Direct2DPaintEngineHelper::operator=+0x00000693

6d0c70d0kso!kpt::RelativeRect::unclipped+0x00001146

6d0c8d0ckso!kpt::VariantImage::forceUpdateCacheImage+0x00000cd5

6d451d5ckso!BlipCacheMgr::BrushCache+0x0000049a

6d451e85kso!BlipCacheMgr::GenerateBitmap+0x0000001d

6d453227kso!BlipCacheMgr::GenCachedBitmap+0x00000083

6d29bb92kso!drawing::PictureRenderLayer::render+0x000009b6

6d450fb1kso!drawing::RenderTargetImpl::paint+0x00000090

6d29b528kso!drawing::PictureRenderLayer::render+0x0000034c

6d2a2d83kso!drawing::VisualRenderer::render+0x00000060

6d2b8970kso!drawing::SingleVisualRenderer::drawNormal+0x000002b5

6d2b86a7kso!drawing::SingleVisualRenderer::draw+0x000001e1

6d2b945ekso!drawing::SingleVisualRenderer::draw+0x00000046

6d3d0142kso!drawing::ShapeVisual::paintEvent+0x0000044a

680a2b5cwpsmain!WpsShapeTreeVisual::getHittestSubVisuals+0x000068f1

6d0e36dfkso!AbstractVisual::visualEvent+0x00000051

6d3cbe97kso!drawing::ShapeVisual::visualEvent+0x0000018f

6d0eba90kso!VisualPaintEvent::arriveVisual+0x0000004e

0:000>dt_DPH_BLOCK_INFORMATIONcc780e18-0x20

verifier!_DPH_BLOCK_INFORMATION

+0x000StartStamp:0xc0c0c0c0

+0x004Heap:0xc0c0c0c0Void

+0x008RequestedSize:0xc0c0c0c0

+0x00cActualSize:0xc0c0c0c0

+0x010Internal:_DPH_BLOCK_INTERNAL_INFORMATION

+0x018StackTrace:0xc0c0c0c0Void

+0x01cEndStamp:0xc0c0c0c0

段中的最后一个堆条目通常是一个空闲块。堆块的状态指示为空闲块,堆块声明前一个块的大小为00108,而当前块的大小为00a30。前一块报告其自身大小为0x20字节,不匹配。位置为05f61000的堆块的使用似乎是该堆块的使用导致以下块的元数据损坏的可能性。堆块如下:

0:000>!heap-a05f60000

IndexAddressNameDebuggingoptionsenabled

1:05f60000

Segmentat05f60000to0605f000(00001000bytescommitted)

Flags:00000002

ForceFlags:00000000

Granularity:8bytes

SegmentReserve:00100000

SegmentCommit:00002000

DeCommitBlockThres:00000200

DeCommitTotalThres:00002000

TotalFreeSize:00000146

Max.AllocationSize:fffdefff

LockVariableat:05f60258

NextTagIndex:0000

MaximumTagIndex:0000

TagEntries:00000000

PsuedoTagEntries:00000000

VirtualAllocList:05f6009c

Uncommittedranges:05f6008c

05f61000:000fe000(1040384bytes)

FreeList[00]at05f600c0:05f605b8.05f605b8

05f605b0:00108.00a30[100]-free

Segment00at05f60000:

Flags:00000000

Base:05f60000

FirstEntry:05f604a8

LastEntry:0605f000

TotalPages:000000ff

TotalUnCommit:000000fe

LargestUnCommit:00000000

UnCommittedRanges:(1)

HeapentriesforSegment00inHeap05f60000

address:psize.sizeflagsstate(requestedsize)

05f60000:00000.004a8[101]-busy(4a7)

05f604a8:004a8.00108[101]-busy(107)Internal

05f605b0:00108.00a30[100]

05f60fe0:00a30.00020[111]-busy(1d)

05f61000:000fe000-uncommittedbytes.

0:000>dd05f60fe0

05f60fe0a9b3c8360300708705f6008c05f6008c

05f60ff005f6003805f6003805f61000000fe000

05f61000????????????????????????????????

05f61010????????????????????????????????

05f61020????????????????????????????????

05f61030????????????????????????????????

05f61040????????????????????????????????

05f61050????????????????????????????????

0x03 披露时间表

该漏洞于2020年8月报告,披露时间表:

  • 2020-08-04-将电子邮件发送到公开提供的WPS的各种邮件列表(销售和支持)。
  • 2020-08-10-WPS团队回应该报告可以转发给他们。
  • 2020-08-11-要求进一步的信息,例如向适当的渠道披露等。
  • 2020-08-17-根据先前的要求与WPS团队进行跟进。
  • 2020-08-18-通过电子邮件提供技术报告和概念验证(未加密)。
  • 2020-08-25-WPS跟进报告进度。
  • 2020-08-26-WPS更新说此问题已转发给开发团队。
  • 2020-08-28-WPS发送了一封电子邮件,指出该问题已在最新的下载版本11.2.0.9403中得到解决。
  • 2020-08-28-针对提供的PoC测试了新版本,并确认问题已解决。
  • 2020-08-28-向WPS团队寻求咨询或更改日志更新。
  • 2020-09-03-申请漏洞CVE。
  • 2020-09-14-已分配CVE编号:CVE-2020-25291。

本文翻

继续浏览有关 安全 的文章
发表评论