这两周一直在看这个漏洞, 坑点实在太多, 多亏了学长的帮助和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中的数据
与蓝色区域内容一致.
参考
https://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0320