这两周一直在看这个漏洞, 坑点实在太多, 多亏了学长的帮助和CVE作者的详细分析, 今天终于有了眉目.

写下来算是对前两周的一个总结, 也是对之后自己的一个激励.

漏洞成因

简单的说就是因为有符号整数比较造成的堆溢出.

先分配一个堆块, 当重用的时候检查后来数据的长度, 也就是有符号整数比较, 那么我们设置后来数据长度足够大, 使得整数下溢. 比较自然通过, 而不重新调整堆块的大小, 后来memcpy大量的数据到堆块中, 造成堆溢出.

调试再现

开头下个断点, 反汇编查看

0:009> u hwpapp+99850 L30
HwpApp!ResetCoreEngine+0x5eb10:
68ef9850 55              push    ebp
68ef9851 8bec            mov     ebp,esp
68ef9853 51              push    ecx
68ef9854 56              push    esi
......
68ef988c 51              push    ecx
68ef988d ff7508          push    dword ptr [ebp+8]
68ef9890 8d4f10          lea     ecx,[edi+10h]
68ef9893 e898b40000      call    HwpApp!ResetCoreEngine+0x69ff0 (68f04d30)  ; [bp1] 用count字段的值来分配所需空间
68ef9898 85c0            test    eax,eax
68ef989a 750c            jne     HwpApp!ResetCoreEngine+0x5eb68 (68ef98a8)
68ef989c 3905a82a6d69    cmp     dword ptr [HwpApp!CHncSDS_Client::`vftable'+0x12cddc (696d2aa8)],eax
68ef98a2 0f8581000000    jne     HwpApp!ResetCoreEngine+0x5ebe9 (68ef9929)
68ef98a8 8b4508          mov     eax,dword ptr [ebp+8]
68ef98ab 85c0            test    eax,eax
68ef98ad 746b            je      HwpApp!ResetCoreEngine+0x5ebda (68ef991a)  ; [bp2] 检查count是否为0, 如是则跳过复制数据
68ef98af 8b16            mov     edx,dword ptr [esi]
68ef98b1 8bce            mov     ecx,esi
68ef98b3 c1e003          shl     eax,3              ; [count*8 为要复制的数据长度]
68ef98b6 50              push    eax
68ef98b7 ff7710          push    dword ptr [edi+10h]
68ef98ba ff5208          call    dword ptr [edx+8]   ; 读取数据并复制进堆中
......

F8跟进[bp1], 查看反汇编

0:009> u HwpApp!ResetCoreEngine+0x69ff0 L50
HwpApp!ResetCoreEngine+0x69ff0:
68f04d30 55              push    ebp
68f04d31 8bec            mov     ebp,esp
68f04d33 53              push    ebx
68f04d34 56              push    esi
68f04d35 57              push    edi
68f04d36 8b7d08          mov     edi,dword ptr [ebp+8]
68f04d39 8bf1            mov     esi,ecx
68f04d3b 85ff            test    edi,edi
68f04d3d 752c            jne     HwpApp!ResetCoreEngine+0x6a02b (68f04d6b)   ; 检查count是否为0, 若是 ret
68f04d3f 8b06            mov     eax,dword ptr [esi]
......
68f04d65 5e              pop     esi
68f04d66 5b              pop     ebx
68f04d67 5d              pop     ebp
68f04d68 c20800          ret     8
68f04d6b 8b16            mov     edx,dword ptr [esi]
68f04d6d 85d2            test    edx,edx
68f04d6f 7545            jne     HwpApp!ResetCoreEngine+0x6a076 (68f04db6)  ; 检查当前buffer是否分配
......
68f04db6 8b4e08          mov     ecx,dword ptr [esi+8]
68f04db9 3bf9            cmp     edi,ecx
68f04dbb 7f30            jg      HwpApp!ResetCoreEngine+0x6a0ad (68f04ded)  ; [!] 漏洞触发点, edi存储当前count值, ecx存储前一个count值
68f04dbd 8b4e04          mov     ecx,dword ptr [esi+4]
68f04dc0 3bf9            cmp     edi,ecx
68f04dc2 0f8eb3000000    jle     HwpApp!ResetCoreEngine+0x6a13b (68f04e7b)  ; 在这里跳过重新分配堆块
........
68f04e86 5d              pop     ebp
68f04e87 c20800          ret     8

在地址为68f04d3d看到, 当前count为0x90000002(0b10010000000000000000000000000010)

0:009> t
eax=00000001 ebx=00000004 ecx=088be68c edx=088bea64 esi=088be68c edi=90000002
eip=68f04d3d esp=088be634 ebp=088be640 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
HwpApp!ResetCoreEngine+0x69ffd:
68f04d3d 752c            jne     HwpApp!ResetCoreEngine+0x6a02b (68f04d6b) [br=1]

68f04dbb, 0x1 > 0x90000002, 所以跳转未实现

0:009> t
eax=00000001 ebx=00000004 ecx=00000001 edx=0568a1e0 esi=088be68c edi=90000002
eip=68f04dbb esp=088be634 ebp=088be640 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
HwpApp!ResetCoreEngine+0x6a07b:
68f04dbb 7f30            jg      HwpApp!ResetCoreEngine+0x6a0ad (68f04ded) [br=0]

继续来到[bp2],

eax=90000002 ebx=00000004 ecx=00000001 edx=0568a1e0 esi=088bea50 edi=088be67c
eip=68ef98ad esp=088be650 ebp=088be65c iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
HwpApp!ResetCoreEngine+0x5eb6d:
68ef98ad 746b            je      HwpApp!ResetCoreEngine+0x5ebda (68ef991a) [br=0]

F8跟进68ef98ba的call, 数据的复制会在这里发生

0:009> u eip L30
HwpApp!CHncAppShield::operator=+0x241800:
......
6924835a 8b450c          mov     eax,dword ptr [ebp+0Ch]  ; 数据的长度
6924835d 53              push    ebx
6924835e 8b5d08          mov     ebx,dword ptr [ebp+8]   
69248361 3bc6            cmp     eax,esi                 
69248363 7e13            jle     HwpApp!CHncAppShield::operator=+0x241838 (69248378)    ; 这里还有一个比较, 将两个count都乘以8
......
69248378 50              push    eax                ; size
69248379 53              push    ebx                ; destination
6924837a 8bcf            mov     ecx,edi
6924837c e8efd3ffff      call    HwpApp!CHncAppShield::operator=+0x23ec30 (69245770) ; 读取文件数据
69248381 5b              pop     ebx
69248382 5f              pop     edi
69248383 5e              pop     esi
69248384 5d              pop     ebp
69248385 c20800          ret     8

69248363地址处看到, 0x90000002*8 = 0x80000010 依然是负数, 这样就成功利用了两个整数下溢

0:009> t
eax=80000010 ebx=0568a1e0 ecx=088bea50 edx=695cf8e8 esi=00000008 edi=088bea50
eip=69248363 esp=088be634 ebp=088be640 iopl=0         nv up ei ng nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000292
HwpApp!CHncAppShield::operator=+0x241823:
69248363 7e13            jle     HwpApp!CHncAppShield::operator=+0x241838 (69248378) [br=1]

6924837c, 可以得到复制的目的地是0x0568a1e0

0:009> t
eax=80000010 ebx=0568a1e0 ecx=088bea50 edx=695cf8e8 esi=00000008 edi=088bea50
eip=6924837c esp=088be62c ebp=088be640 iopl=0         nv up ei ng nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000292
HwpApp!CHncAppShield::operator=+0x24183c:
6924837c e8efd3ffff      call    HwpApp!CHncAppShield::operator=+0x23ec30 (69245770)

之后数据会调用memcpy函数, 数据溢出

0:009> g
(b74.c54): C++ EH exception - code e06d7363 (first chance)
(b74.c54): C++ EH exception - code e06d7363 (!!! second chance !!!)
eax=088be564 ebx=80000010 ecx=00000003 edx=00000000 esi=6b1bc7fc edi=088be604
eip=75769617 esp=088be564 ebp=088be5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
KERNELBASE!RaiseException+0x54:
75769617 c9              leave

查看缓冲区

0:009> dd 0568a1e0 L30
0568a1e0  fffffffe efffffff 0ac00417 0000000c
0568a1f0  00320000 ffffffff 005e0003 002e0031
0568a200  0000010c 00320000 ffffffff 005e0003
0568a210  002e0032 0000000c 00320000 ffffffff
0568a220  005e0003 00290033 0000010c 00320000
0568a230  ffffffff 005e0003 00290034 0000000c
0568a240  00320000 ffffffff 00280004 0035005e
0568a250  010c0029 00000000 ffff0032 0004ffff
0568a260  005e0028 00290036 0000002c 00320000
0568a270  ffffffff 005e0002 00000037 00000001
0568a280  00000001 00000001 00000001 00000001
0568a290  00000001 00000001 03600419 00000180

对比poc中的数据

CVE

与蓝色区域内容一致.

参考

https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0320