WPS 2012/2013 RTF fchars 堆溢出漏洞分析
by phperl ,zzf,nine9 of code audit labs of vulnhunt.com
在2013年9月3日,翰海源捕获到一个针对中国政府部门的钓鱼邮件定向攻击事件。我们发现该攻击利用了一个WPS 2012/2013的0day漏洞,之后我们在第一时间通知被攻击客户,以及联系金山WPS相关部门处理该问题。金山WPS对于我们反映的问题,做出迅速响应,与昨天(2013年12月10日)修补该漏洞,并发布新的版本WPS。请大家及时更新WPS的最新版本,下面是我们针对本次0day攻击事件的技术分析一些摘要。
- 问题描述
在WPS处理rtf时,遇到\*\fchars元素时处理不当,导致堆溢出,成功利用,攻击者可以执行任意代码。
- 漏洞分析
在处理rtf时,遇到\*\fchars元素时会将其后形如\u16705格式的字符串转换成字符后使用wcscat追加到缓冲区末尾,直到遇到‘}’字符为止,由于没有检测字符串的长度,导致heap缓冲区溢出。缓冲区位于C++对象的内部,紧跟在缓冲区后面的是虚表指针,覆盖了虚表指针后,可以跳转到用户控制的地址,构造ROP绕过DEP可以执行任意代码。
asm code in rtfreader.dll 9.1.0.4249 .text:10033296 mov [ebp+arg_0], eax .text:10033299 call ds:isalpha .text:1003329F pop ecx .text:100332A0 test eax, eax .text:100332A2 jz short loc_1003326B .text:100332A4 lea eax, [ebp+var_1C] .text:100332A7 push eax .text:100332A8 push ebx .text:100332A9 call read_num //ebp-1c为转换前的字符串长度 .text:100332AE cmp [ebp+var_1C], 496h .text:100332B5 jnz short loc_100332C8 .text:100332B7 push [ebp+var_18] //ebp-18为转换后的字符 .text:100332BA push [ebp+arg_4] .text:100332BD push ebx .text:100332BE call sub_10032A2C .text:100332C3 jmp loc_1003304E .text:10011D2C ; int __stdcall sub_10011D2C(int, void *Src, int) .text:10011D2C sub_10011D2C proc near ; DATA XREF: .rdata:10038644o .text:10011D2C ; .rdata:100386F8o .text:10011D2C .text:10011D2C Dst = dword ptr -20h .text:10011D2C var_C = dword ptr -0Ch .text:10011D2C var_4 = dword ptr -4 .text:10011D2C arg_0 = dword ptr 8 .text:10011D2C Src = dword ptr 0Ch .text:10011D2C arg_8 = dword ptr 10h .text:10011D2C .text:10011D2C push ebp .text:10011D2D mov ebp, esp .text:10011D2F sub esp, 20h .text:10011D32 mov eax, ___security_cookie .text:10011D37 xor eax, ebp .text:10011D39 mov [ebp+var_4], eax .text:10011D3C mov eax, [ebp+Src] .text:10011D3F push esi .text:10011D40 push [ebp+arg_8] ; Dst .text:10011D43 mov esi, [ebp+arg_0] .text:10011D46 push eax ; Src .text:10011D47 lea ecx, [ebp+Dst] .text:10011D4A call sub_1000FF9B .text:10011D4F cmp [ebp+var_C], 8 .text:10011D53 mov eax, [ebp+Dst] .text:10011D56 jnb short loc_10011D5B .text:10011D58 lea eax, [ebp+Dst] .text:10011D5B .text:10011D5B loc_10011D5B: ; CODE XREF: sub_10011D2C+2Aj .text:10011D5B push eax ; Source .text:10011D5C mov eax, [esi+8] .text:10011D5F add eax, 18D8h .text:10011D64 push eax ; Dest .text:10011D65 call ds:wcscat //追加到缓冲区末尾,此处会导致缓冲区溢出 .text:10011D6B pop ecx .text:10011D6C pop ecx .text:10011D6D push 0 ; int .text:10011D6F push 1 ; char
漏洞利用分析
利用上面的方式在堆上填充大量0×030a4a2c,该地址为指向dbghelp.dll中切换堆栈的指令,将堆栈切换到发生溢出的缓冲区中,从而利用dbghelp.dll中的指令构造ROP绕过DEP。
0:000> u 030a4a2c dbghelp!dia::CDiaLoadCallback::InitExeRead+0x43: 030a4a2c 95 xchg eax,ebp 030a4a2d c0eb02 shr bl,2 030a4a30 b001 mov al,1 030a4a32 5e pop esi 030a4a33 c9 leave 030a4a34 c3 ret
溢出的缓冲区的内容为:
- ROP链
- Shellcode
- NOP指令0×41414141
- 0×030a4a2c在堆中的地址
重现方法如下:
ba e1 030a4a2c eax=035fbc48 ebx=03648008 ecx=00200404 edx=035d65e7 esi=0012cda0 edi=00000071 eip=030a4a2c esp=0012ccdc ebp=0012ccf8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 dbghelp!MiniDumpReadDumpStream+0x4eb7c: 030a4a2c 95 xchg eax,ebp 0:000> db eax-20 035fbc28 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 035fbc38 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 035fbc48 04 04 20 00 8e c5 0b 03-f0 f4 ff ff 84 0f 0c 03 .. ............. 035fbc58 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 035fbc68 00 00 00 00 58 9c f1 01-00 00 00 00 00 00 00 00 ....X........... 0:000> dd 200404 00200404 030a4a2c 030a4a2c 030a4a2c 030a4a2c 00200414 030a4a2c 030a4a2c 030a4a2c 030a4a2c 00200424 030a4a2c 030a4a2c 030a4a2c 030a4a2c
ROP链分析
将堆栈指针切换到溢出缓冲区
执行VirtualProtect,将栈变为可执行
返回到jmp esp指令,开始执行shellcode
- shellcode分析
先是shellcode解码头
035fb254 0fb6c0 movzx eax,al 035fb257 01c7 add edi,eax 035fb259 3017 xor byte ptr [edi],dl ds:0023:035fb260=f5 035fb25b 47 inc edi 035fb25c 49 dec ecx 035fb25d 75fa jne 035fb259
循环遍历句柄,调用GetFileSize,成功则调用CreateFileMapping、MapViewOfFile,搜索11222211和33444433,找到则开始解密,将解密后的内容写入临时文件的IE7.exe文件,并使用WinExec执行该文件,执行后会释放到临时目录win32_453B.dll。
shellcode使用LoadLibrary加载释放的dll,并调用dll导出函数mail。