word嵌⼊对象依损坏_CVE202025291:⾦⼭WPSOffice远程
堆损坏漏洞分析
更多全球⽹络安全资讯尽在⾢安全
前⾔
这部分是关于WPS Office的简介,外国⼈⾃是要简单了解⼀下的,⾄于国⼈,duck不必吧,因此略去。
WPS Office软件中存在⼀个远程执⾏代码漏洞,对于特制的Office⽂件,不正确处理内存中的对象会触发此漏洞。利⽤此漏洞可以在当前⽤户的上下⽂中运⾏任意代码。但是利⽤不成功的话,可能会导致拒绝服务。
漏洞产品:WPS Office
影响版本:11.2.0.9453。
漏洞分析
Qt 类库⾥⼤量的类根据功能分为各种模块,这些模块⼜分为以下⼏⼤类:
Qt 基本模块(Qt Essentials):提供了 Qt 在所有平台上的基本功能。
Qt 附加模块(Qt Add-Ons):实现⼀些特定功能的提供附加价值的模块。
增值模块(Value-AddModules):单独发布的提供额外价值的模块或⼯具。
技术预览模块(Technology Preview Modules):⼀些处于开发阶段,但是可以作为技术预览使⽤的模块。
Qt ⼯具(Qt Tools):帮助应⽤程序开发的⼀些⼯具。
基本模块中,有⼀个名为Qt Core的模块,主要提供核⼼的⾮ GUI 功能,所有模块都需要这个模块。这个模块的类包括了动画框架、定时器、各个容器类、时间⽇期类、事件、IO、JSON、插件机制、智能指针、图形(矩形、路径等)、线程、XML 等。所有这些类都可以通过 头⽂件引⼊。
(这段是我⾃⼰的资料)
WPS Office的Qt模块(⽤于图像格式解析)中发现堆损坏,嵌⼊特制的WPS office图⽚可能会触发此漏洞。WPS在打开特制的⽂档⽂件时,将触发访问冲突:EDX指向数组,⽽EAX却指向数组的索引。
0:000> g
(c50.b4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=000000c0 ebx=006f1c48 ecx=cd2aefbc edx=cd2c6f80 esi=2ed7ae18 edi=0000001c
eip=6ba13321 esp=006f1b44 ebp=006f1b44 iopl=0        nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b            efl=00210202
QtCore4!QMatrix::dy+0x48a8:
6ba13321 8b448210        mov    eax,dword ptr [edx+eax*4+10h] ds:002b:cd2c7290=
崩溃是如何触发的?让我们看⼀下PNG的header格式。
00029E30  FF 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44  ÿ‰IHD
00029E40  52 00 00 02 80 00 00 01 C6 04 03 00 00 00 16 0A  R...€...Æ.......
00029E50  27 FC 00 00 00 04 67 41 4D 41 00 00 B1 88 95 98  'ü....gAMA..±ˆ•˜
00029E60  F4 A6 00 00 00 30 50 4C 54 45 00 00 00 80 00 00  ô¦...€..
00029E70  00 80 00 80 80 00 00 00 80 80 00 80 00 80 80 80  .€.€€...€€.€.€€€
00029E80  80 80 C0 C0 C0 FF 00 00 00 FF 00 FF FF 00 00 00  €€ÀÀÀÿ...ÿ.ÿÿ...
00029E90  FF FF 00 FF 00 FF FF FF FF FF 7B 1F B1 C4 00 00  ÿÿ.ÿ.ÿÿÿÿÿ{.±Ä..
偏移量0x29E31-0x29E34是PNG⽂件格式的签名标头。PNG头⽂件的结构为:
PNG签名 --> IHDR --> gAMA --> PLTE --> pHYs --> IDAT --> IEND
当Word⽂档中存在嵌⼊式的PNG⽂件时,WPS Office Suite会使⽤QtCore库解析该PLTE结构,从⽽触发堆损坏。该漏洞位于偏移
量0x29E82到0x29E85之间,具体为调⾊板的解析失败,从⽽触发了堆中的内存损坏。
崩溃触发之前的堆栈跟踪如下:
00 00ee1790 6b8143ef QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a71
01 00ee17f0 6b814259 QtCore4!QBrush::setMatrix+0x234
02 00ee58d4 6b8249a4 QtCore4!QBrush::setMatrix+0x9e
03 00ee58ec 6b80cc84 QtCore4!QImage::rect+0x22b
04 00ee5908 6b857ccc QtCore4!QTransform::inverted+0xec8
05 00ee629c 6b81c55b QtCore4!QSvgFillStyle::setFillOpacity+0x1b59
06 00ee6480 6b896844 QtCore4!QPainter::drawPixmap+0x1c98
07 00ee6574 6d1e0fbd QtCore4!QPainter::drawImage+0x325
08 00ee6594 6d0dd155 kso!GdiDrawHoriLineIAlt+0x11a1a
在QtCore4解析嵌⼊式图像之前,KSO模块调⽤了kso!GdiDrawHoriLineIAlt。使⽤IDA Pro分析发⽣异常处的函数,发现最后的崩溃路径如下(来⾃WinDBG):
QtCore4!QMatrix::dy+0x48a8:
6ba13321 8b448210        mov    eax,dword ptr [edx+eax*4+10h] ds:002b:cd2c7290=
IDA Pro反汇编该函数:
.text:67353315                push    ebp
.text:67353316                mov    ebp, esp
.text:67353318                movzx  eax, byte ptr [ecx+edx]  ; **crash here**
.text:6735331C                mov    ecx, [ebp+arg_0]
.text:6735331F                mov    edx, [ecx]
.text:67353321                mov    eax, [edx+eax*4+10h]
.text:67353325                mov    ecx, eax
从崩溃点转储的信息得知,应⽤程序在0x67353321处(mov eax, [edx+eax*4+10h])触发了访问冲突。可以看到,EAX寄存器的值为0xc0。因此,从这⾥可以根据导致异常的指令对寄存器的状态进⾏⼀些假设。尤为需要注意的是,在发⽣异常之前,我们可以看到ECX(0xc0)的值被写⼊到以下指令所定义的任意位置:
mov    ecx, [ebp+arg_0]
此外,除了故障指令,EBP的偏移量存储在ECX中。我们在前⾯提到的指令(偏移量为0x6ba1331c)上设置了⼀个断点,以观察内存使⽤情况。可以看到,断点触发后,第⼀个值c45adfbc指向另⼀个指针,⽽它本该指向某个数组。
Breakpoint 0 hit
eax=0000000f ebx=004f1b40 ecx=d3544100 edx=0000001c esi=d1200e18 edi=0000001c
eip=6ba1331c esp=004f1a34 ebp=004f1a34 iopl=0        nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b            efl=00200202
QtCore4!QMatrix::dy+0x48a3:
6ba1331c 8b4d08          mov    ecx,dword ptr [ebp+8] ss:002b:004f1a3c=c45adfbc
0:000> dc ebp+8
004f1a3c  c45adfbc 00000048 00000000 6f13830f  ..
004f1a4c  004f5cc8 00000000 00000000 00000000  .\O.............
004f1a5c  00000000 004f65a0 004f662c 00000000  .....eO.,fO.....
004f1a6c  779eae8e 00000000 00000001 3f800000  ...w...........?
004f1a7c  3f800000 3f31e4f8 3f800000 3f800000  ...?..1?...?...?
004f1a8c  3f800000 3f31e4f8 3f800000 3de38800  ...?..1?...?...=
004f1a9c  3de38800 3d9e1c8a 3c834080 004f3c00  ...=...=.@.<.>004f1aac  4101c71c 6ba13315 3f800000 4081c71c  ...A.?...@
继续跟踪c45adfbc处的内存引⽤,发现另⼀个指针。第⼀个值ab69cf80通常表⽰为指向它所引⽤的任何地⽅的指针。因⽽,指
针ab69cf80基本上是我们指针的索引数组。
0:000> dc c45adfbc
c45adfbc  ab69cf80 d3544100 00000003 00000280  ..i..AT.........
c45adfcc  0000055a 00000012 c0c0c0c0 1c3870e2  Z (8)
c45adfdc  40ad870e 1c3870e2 40ad870e 00000000  ...@.p8....@....
c45adfec  00000000 c0c0c0c1 6c1d12c0 00000000  ...........l....
c45adffc  c0c0c0c0 ???????? ???????? ????????  ....????????????
c45ae00c  ???????? ???????? ???????? ????????  ????????????????
c45ae01c  ???????? ???????? ???????? ????????  ????????????????
c45ae02c  ???????? ???????? ???????? ????????  ????????????????
0:000> dc ab69cf80
ab69cf80  00000001 0000001c 00000010 00000001  ................ // 0000001c is overwritten in the register EDX and EDI before we trigger crash
//在触发崩溃点之前,寄存器EDX和EDI就被0000001c覆盖
ab69cf90  ff000000 ff800000 ff008000 ff808000  ................
ab69cfa0  ff000080 ff800080 ff008080 ff808080  ................
ab69cfb0  ffc0c0c0 ffff0000 ff00ff00 ffffff00  ................ // ffc0c0c0 where it will be stored in EAX after crash, at the moment it only takes 0xf value in EAX //ffc0c0c0处的值会被存储在EAX中,⽬前它的值为0xf.
ab69cfc0  ff0000ff ffff00ff ff00ffff ffffffff  ................
ab69cfd0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
ab69cfe0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
ab69cff0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
因为知道程序崩溃的路径,所以可以使⽤下⾯的命令简单地设置⼀个断点。该命令将获得edx+eax*4+10的指针值,并检查其是否满⾜
0xc0。
bp 6ba13321 ".if (poi(edx+eax*4+10) == 0xc0) {} .else {gc}"
0:000> g
eax=000000c0 ebx=004f1b40 ecx=c45adfbc edx=ab69cf80 esi=d1200e18 edi=0000001c
eip=6ba13321 esp=004f1a34 ebp=004f1a34 iopl=0        nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b            efl=00200202
QtCore4!QMatrix::dy+0x48a8:
6ba13321 8b448210        mov    eax,dword ptr [edx+eax*4+10h] ds:002b:ab69d290=
如果观察堆栈,可以看到执⾏情况如下:
004f1a38 6ba3cb98 QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74
004f1a3c c45adfbc
004f1a40 00000048
004f1a44 00000000
004f1a48 6f13830f verifier!DphCommitMemoryForPageHeap+0x16f
004f1a4c 004f5cc8
004f1a50 00000000
004f1a54 00000000
004f1a58 00000000
004f1a5c 00000000
004f1a60 004f65a0
004f1a64 004f662c
004f1a68 00000000
004f1a6c 779eae8e ntdll!RtlAllocateHeap+0x3e
如果我们反汇编6ba3cb98,则可以看到以下反汇编代码,为该漏洞真正的起因所在:
6ba3cb89 8b96b4000000    mov    edx,dword ptr [esi+0B4h]
6ba3cb8f 8b4df4          mov    ecx,dword ptr [ebp-0Ch]
6ba3cb92 52              push    edx
6ba3cb93 8bd7            mov    edx,edi
6ba3cb95 ff5580          call    dword ptr [ebp-80h]
6ba3cb98 8b4e7c          mov    ecx,dword ptr [esi+7Ch]
C pseudo code
grad = *(&ptr_grad);
if ( grad > 0.0099999998 )
{
input_value = grad_size(check, size, input);
ptr_grad = *(input);
... cut here ...
我们在6ba3cb89地址上设置断点并观察ESI+0xB4,可以看到指向另⼀个位置的指针:
0:000> r
eax=00000000 ebx=00791878 ecx=00000005 edx=00793938 esi=cb07de18 edi=0000001c
eip=6ba3cb89 esp=00791780 ebp=00791870 iopl=0        nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b            efl=00200202
QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a65:
6ba3cb89 8b96b4000000    mov    edx,dword ptr [esi+0B4h] ds:002b:cb07decc=cf69afbc
0:000> dc esi+0B4h
cb07decc  cf69afbc c0c0c000 00000000 00000100  ..i.............
cve漏洞库
cb07dedc  c0c0c0c0 00000000 00000000 00000000  ................
cb07deec  00000000 00000000 00000000 00000000  ................
cb07defc  00000000 cf030fd0 00000000 00000000  ................
cb07df0c  00000000 00000000 00000000 00000000  ................
cb07df1c  c0c0c0c0 00000000 3ff00000 00000000  ...........?....
cb07df2c  00000000 00000000 00000000 00000000  ................
cb07df3c  00000000 00000000 3ff00000 00000000  ...........?....
0:000> dc cf69afbc
cf69afbc  c88baf80 d1326100 00000003 00000280  .....a2.........
cf69afcc  0000055f 00000012 c0c0c0c0 1c3870e2  _ (8)
cf69afdc  40ad870e 1c3870e2 40ad870e 00000000  ...@.p8....@....
cf69afec  00000000 c0c0c0c1 6c1d12c0 00000000  ...........l....
cf69affc  c0c0c0c0 ???????? ???????? ????????  ....????????????
cf69b00c  ???????? ???????? ???????? ????????  ????????????????
cf69b01c  ???????? ???????? ???????? ????????  ????????????????
cf69b02c  ???????? ???????? ???????? ????????  ????????????????
0:000> dc c88baf80
c88baf80  00000001 0000001c 00000010 00000001  ................
c88baf90  ff000000 ff800000 ff008000 ff808000  ................
c88bafa0  ff000080 ff800080 ff008080 ff808080  ................
c88bafb0  ffc0c0c0 ffff0000 ff00ff00 ffffff00  ................
c88bafc0  ff0000ff ffff00ff ff00ffff ffffffff  ................
c88bafd0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
c88bafe0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
c88baff0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
从这⾥我们可以知道,代码实际上并没有从指针释放任何东西。⼀旦指向EDX,EDX将保留指向索引数组的指针:
eax=00000000 ebx=00791878 ecx=00000005 edx=cf69afbc esi=cb07de18 edi=0000001c
eip=6ba3cb8f esp=00791780 ebp=00791870 iopl=0        nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b            efl=00200202
QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a6b:
6ba3cb8f 8b4df4          mov    ecx,dword ptr [ebp-0Ch] ss:002b:00791864=d1326100
0:000> dc cf69afbc
cf69afbc  c88baf80 d1326100 00000003 00000280  .....a2.........
cf69afcc  0000055f 00000012 c0c0c0c0 1c3870e2  _ (8)
cf69afdc  40ad870e 1c3870e2 40ad870e 00000000  ...@.p8....@....
cf69afec  00000000 c0c0c0c1 6c1d12c0 00000000  ...........l....
cf69affc  c0c0c0c0 ???????? ???????? ????????  ....????????????
cf69b00c  ???????? ???????? ???????? ????????  ????????????????
cf69b01c  ???????? ???????? ???????? ????????  ????????????????
cf69b02c  ???????? ???????? ???????? ????????  ????????????????
0:000> dc c88baf80
c88baf80  00000001 0000001c 00000010 00000001  ................
c88baf90  ff000000 ff800000 ff008000 ff808000  ................
c88bafa0  ff000080 ff800080 ff008080 ff808080  ................
c88bafb0  ffc0c0c0 ffff0000 ff00ff00 ffffff00  ................
c88bafc0  ff0000ff ffff00ff ff00ffff ffffffff  ................
c88bafd0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
c88bafe0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
c88baff0  c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0  ................
崩溃后的堆栈跟踪:
0:000> kvL
# ChildEBP RetAddr  Args to Child
00 012f18d4 6ba3cb98 cc53afbc 00000048 00000000 QtCore4!QMatrix::dy+0x48a8
01 012f19d0 6b8143ef 00000000 012f1b78 012f1a5c QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74
02 012f1a30 6b814259 0000002e 012f5bd0 00000000 QtCore4!QBrush::setMatrix+0x234
03 012f5b14 6b8249a4 0000003b 012f5b68 cc780e18 QtCore4!QBrush::setMatrix+0x9e
04 012f5b2c 6b80cc84 0000003b 012f5b68 cc780e18 QtCore4!QImage::rect+0x22b
05 012f5b48 6b857ccc 0000003b 012f5b68 cc780e18 QtCore4!QTransform::inverted+0xec8
06 012f64dc 6b81c55b 00000000 003c0000 00000000 QtCore4!QSvgFillStyle::setFillOpacity+0x1b59
07 012f66c0 6b896844 012f6724 cc818ff0 0000001c QtCore4!QPainter::drawPixmap+0x1c98
08 012f67b4 6d1e0fbd 012f69ec 012f66d4 012f6864 QtCore4!QPainter::drawImage+0x325
09 012f67d4 6d0dd155 012f6a54 012f69ec 012f6864 kso!GdiDrawHoriLineIAlt+0x11a1a
0a 012f67ec 6d0c8d88 012f69ec 012f68e0 012f6864 kso!kpt::PainterExt::drawBitmap+0x23
堆分析:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。