首页
社区
课程
招聘
[原创]IE0DAY:iepeers.dll!CPersistUserData::setAttribute导致内存破坏可能导致远程执行任意代码
发表于: 2010-3-12 08:33 31605

[原创]IE0DAY:iepeers.dll!CPersistUserData::setAttribute导致内存破坏可能导致远程执行任意代码

2010-3-12 08:33
31605
收藏
免费 7
支持
分享
最新回复 (46)
雪    币: 11678
活跃值: (158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
26
强悍,没的说的。。。
2010-3-13 20:43
0
雪    币: 822
活跃值: (380)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
27
谢谢小聪大大能将自己的经验之谈无私的分享给我们,在下看了之后真是受益匪浅!如果大家都能这样无私的分享自己的心得,那大家的水平一定都能够进步更快的.

最后对于VariantChangeTypeEx这个函数的调用,在下认为其实这个调用是成功的,确实把window对象转成了string,结果是"[object]"

那为什么还会调用VariantClear呢?其实很简单,应为调用VariantChangeTypeEx时pvargDest和pvargSrc是相同的,即转换是原地实现的,那么在把结果写到pvargDest里面之前,必须先把pvargSrc(也就是pvargDst)先释放,于是就有了VariantClear的调用
77126b4f 66833808        cmp     word ptr [eax],8
77126b53 0f833de2ffff    jae     OLEAUT32!VariantChangeTypeEx+0x1016 (77124d96) [br=1]
当type<8时,由于都是简单对象类型,没有对象指针需要释放引用计数,所以也不需要调用clear了
2010-3-14 13:27
0
雪    币: 42
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
28
学习一下 嘿嘿··
2010-3-14 20:44
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
29
[QUOTE=古河;775461]最后对于VariantChangeTypeEx这个函数的调用,在下认为其实这个调用是成功的,确实把window对象转成了string,结果是"[object]"

那为什么还会调用VariantClear呢?其实很简单,应为调用VariantChangeTypeEx时pvargDest和pvargSrc是相同的,即转换是原地实现的,那么在把结果写到pvargDest里面之前,必须先把pvargSrc(也就是pvargDst)先释放,于是就有了VariantClear的调用
77126b4f 66833808        cmp     word ptr [eax],8
77126b53 0f833de2ffff    jae     OLEAUT32!VariantChangeTypeEx+0x1016 (77124d96) [br=1]
当type<8时,由于都是简单对象类型,没有对象指针需要释放引用计数,所以也不需要调用clear了[/QUOTE]
重新看了一下我的记录,的确基本如你所说:
0:005> t
eax=00000061 ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f578f esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf2:
770f578f 8d45d0 lea eax,[ebp-30h]
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5792 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf5:
770f5792 50 push eax
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5793 esp=019dc92c ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf6:
770f5793 ff7510 push dword ptr [ebp+10h] ss:0023:019dc97c=00000409
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5796 esp=019dc928 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf9:
770f5796 56 push esi
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5797 esp=019dc924 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xfa:
770f5797 e8b3be0400 call OLEAUT32!ExtractValueProperty (7714164f)
0:005> p
eax=00000000 ebx=00000000 ecx=001c821c edx=00000010 esi=001897e0 edi=00000000
eip=770f579c esp=019dc930 ebp=019dc96c iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
OLEAUT32!VariantChangeTypeEx+0xff:
770f579c 8bd8 mov ebx,eax

可以看到OLEAUT32!ExtractValueProperty确实是成功了的。因此我起先认为是失败了的确是有误的。

不过对于为什么要释放,显然不是先要把pvargSrc释放掉,因为它的释放操作是直接针对pvargDest的,而并不管它是否与pvargSrc一致。
原因应该是这样的,由于pvarDest在此之前是有意义的,在把新的结果写入pvargDest之前,要把原有的pvargDest内容先清除,于是就对pvargDest调用了VariantClear。
同时,这里对于VariantChangeTypeEx而言,看起来是否是“原地转换”并没有什么影响,因为它并没有判断pvargSrc和pvarDest是否是一致的,因此这个VariantClear调用对于VariantChangeTypeEx而言是“例行”的调用,并不是因为“转换是原地实现的”它才会调用VariantClear。

这里我们再更深入地从参数的性质上来分析,实际上其根源是在于,作为调用者的CPersistUserData::setAttribute,误解了VariantChangeTypeEx的参数pvarDest的意义。

具体地说,在CPersistUserData::setAttribute(或者说写这份代码的程序员)的眼里:
1. pvargSrc是个in参数,VariantChangeTypeEx仅读取其内容;而pvargDest是个out参数,VariantChangeTypeEx只把它当成一个写入结果的新缓冲区,在此之前pvargDest里面的内容无关紧要。而且,VariantChangeTypeEx是在先完成了对pvargSrc中内容的读取和利用后,才对pvargDest进行写入的。

2. 对于CPersistUserData::setAttribute来说,pvargSrc这个Variant的内容也是in参数,原先仅是为了提供转换的源参数,在调用VariantChangeTypeEx(或者其他转换函数)之后,源参数的内容就不再使用了。而VariantChangeTypeEx调用后的pvargDest其实只是一个中间数据,后面可以看到程序又把它拷贝到堆栈的缓冲区中,然后把后者用来调用类函数写入XML文件。

在这种指导思想下,CPersistUserData::setAttribute的代码把对VariantChangeTypeEx的调用的pvargDest和pvargSrc指定成一致,这样一来作为CPersistUserData::setAttribute的输入参数的pvargSrc,在原来内容被销毁的同时,又被作为函数运行的中间数据的缓冲区被输入了新的内容,这种把后面不再利用到的输入参数直接改为用来保存中间数据的方法,是程序优化的一种常用的方法。(注意,这里我的意思是,程序把两个参数指定为一致的意图,“并非追求这个一致本身”,因为按我的理解,pvargSrc对于CPersistUserData::setAttribute来说是VARIANT类型数据整个压入栈中的直接传递,并不是一个供它返回结果的in_out指针参数)

但是问题在于,对VariantChangeTypeEx的这种认知是错误的,其实pvargDest在本质上是一个in_out参数,VariantChangeTypeEx并不是不读取pvargDest指向的旧内容,恰恰相反,它并不把pvargDest指向的位置单纯当成一个缓冲区,而是仍然把它当成一个拥有旧内容的Variant。为了“合理”地清除旧内容,它会判断旧内容是否需要额外的释放,当pvargDest原先关联了一个类对象的时候,它将调用VariantClear去清理pvargDest,这将导致该类对象的访问计数减少。

pvargDest参数是in_out参数的本质,代表着它在调用VariantChangeTypeEx前的值将会影响到它实际的动作,因此在调用VariantChangeTypeEx前该参数指向的Variant的实际内容并非不重要。

如果我以上的分析是正确的,这导致两点:

1. 即使不将pvargDest指定为pvargSrc,而是指定为与其他的有效类对象相关联,调用VariantChangeTypeEx仍然会导致pvargDest关联的类对象被释放。因此这实际上与pvargSrc是什么无关,而只与pvargDest是什么相关。这意味着即使调用者已经不会再使用一个与类对象关联的Variant的原内容了,也仍然不能在未妥善处理它的旧内容时随便地直接将它的指针作为pvargDest参数传给VariantChangeTypeEx去用于接受新的内容,除非调用者本来就想要通过这个操作来释放这个对象。

2. 这同样意味着,在调用VariantChangeTypeEx时,pvargDest指向的Variant的内容不能是未初始化的,否则VariantChangeTypeEx有可能对一个未初始化的Variant(只要它的vt位置的值看起来大于8)调用VariantClear从而导致不可预知的问题
2010-3-15 04:07
0
雪    币: 88
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
30
顶一下,学习了,谢谢楼主
2010-3-15 20:53
0
雪    币: 207
活跃值: (382)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
31
jmp 0x0f09f883,中的地址貌似在我机器上就是0xe8xxxxxx,不知道为什么地址这么高啊,无法覆盖到
2010-3-15 21:23
0
雪    币: 70
活跃值: (74)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
32
简单跟了一下IE8的,发现setattribute其中的VariantChangeTypeEx函数参数不一样,所以不会出现引用计数减1,估计是微软早发现这个问题,然后在IE8中修复了这个问题,下面是IE8的setattribte代码:
03A52509    8BFF            mov     edi, edi
03A5250B    55              push    ebp
03A5250C    8BEC            mov     ebp, esp
03A5250E    83EC 10         sub     esp, 10
03A52511    53              push    ebx
03A52512    56              push    esi
03A52513    57              push    edi
03A52514    33FF            xor     edi, edi
03A52516    397D 0C         cmp     dword ptr [ebp+C], edi
03A52519    75 0A           jnz     short 03A52525
03A5251B    BE 57000780     mov     esi, 80070057
03A52520    E9 C7000000     jmp     03A525EC
03A52525    8B5D 08         mov     ebx, dword ptr [ebp+8]
03A52528    57              push    edi
03A52529    E8 4119FFFF     call    03A43E6F
03A5252E    8BF0            mov     esi, eax
03A52530    3BF7            cmp     esi, edi
03A52532    0F85 B4000000   jnz     03A525EC
03A52538    397B 18         cmp     dword ptr [ebx+18], edi
03A5253B    0F84 AB000000   je      03A525EC
03A52541    8B45 10         mov     eax, dword ptr [ebp+10]
03A52544    66:83F8 08      cmp     ax, 8
03A52548    897D F0         mov     dword ptr [ebp-10], edi
03A5254B    897D F4         mov     dword ptr [ebp-C], edi
03A5254E    897D F8         mov     dword ptr [ebp-8], edi
03A52551    74 62           je      short 03A525B5
03A52553    66:3D 0840      cmp     ax, 4008
03A52557    74 5C           je      short 03A525B5
03A52559    66:83F8 0B      cmp     ax, 0B
03A5255D    74 24           je      short 03A52583
03A5255F    66:3D 0B40      cmp     ax, 400B
03A52563    74 24           je      short 03A52589
03A52565    6A 08           push    8
03A52567    57              push    edi
03A52568    68 09040000     push    409
03A5256D    8D45 10         lea     eax, dword ptr [ebp+10]                      ;参数
03A52570    50              push    eax
03A52571    8D45 F0         lea     eax, dword ptr [ebp-10]                        ;局部变量
03A52574    50              push    eax
03A52575    FF15 5812A403   call    dword ptr [<&OLEAUT32.#147>]     ; OLEAUT32.VariantChangeTypeEx
03A5257B    8BF0            mov     esi, eax
03A5257D    3BF7            cmp     esi, edi
03A5257F    74 2F           je      short 03A525B0
03A52581    EB 5F           jmp     short 03A525E2
03A52583    0FB745 18       movzx   eax, word ptr [ebp+18]
03A52587    EB 07           jmp     short 03A52590
03A52589    8B45 18         mov     eax, dword ptr [ebp+18]
03A5258C    0FB740 08       movzx   eax, word ptr [eax+8]
03A52590    66:3BC7         cmp     ax, di
03A52593    66:C745 F0 0800 mov     word ptr [ebp-10], 8
03A52599    74 07           je      short 03A525A2
03A5259B    68 8C8BA403     push    03A48B8C                         ; UNICODE "true"
03A525A0    EB 05           jmp     short 03A525A7
03A525A2    68 988BA403     push    03A48B98                         ; UNICODE "false"
03A525A7    FF15 4C12A403   call    dword ptr [<&OLEAUT32.#2>]       ; OLEAUT32.SysAllocString
03A525AD    8945 F8         mov     dword ptr [ebp-8], eax
03A525B0    8D75 F0         lea     esi, dword ptr [ebp-10]
03A525B3    EB 0C           jmp     short 03A525C1
03A525B5    66:A9 0040      test    ax, 4000
03A525B9    8B75 18         mov     esi, dword ptr [ebp+18]
03A525BC    75 03           jnz     short 03A525C1
03A525BE    8D75 10         lea     esi, dword ptr [ebp+10]
03A525C1    8B43 18         mov     eax, dword ptr [ebx+18]
03A525C4    8B08            mov     ecx, dword ptr [eax]
03A525C6    83EC 10         sub     esp, 10
03A525C9    8BFC            mov     edi, esp
03A525CB    FF75 0C         push    dword ptr [ebp+C]
03A525CE    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525CF    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D0    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D1    50              push    eax
03A525D2    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D3    FF91 B4000000   call    dword ptr [ecx+B4]
03A525D9    8BF0            mov     esi, eax
03A525DB    83FE 01         cmp     esi, 1
03A525DE    75 02           jnz     short 03A525E2
03A525E0    33F6            xor     esi, esi
03A525E2    8D45 F0         lea     eax, dword ptr [ebp-10]
03A525E5    50              push    eax
03A525E6    FF15 4012A403   call    dword ptr [<&OLEAUT32.#9>]       ; OLEAUT32.VariantClear
03A525EC    5F              pop     edi
03A525ED    8BC6            mov     eax, esi
03A525EF    5E              pop     esi
03A525F0    5B              pop     ebx
03A525F1    C9              leave
03A525F2    C2 1800         retn    18
2010-3-16 09:57
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
33
从中可以看出,这一段就是对的:
03A52514 33FF xor edi, edi
......
03A52548 897D F0 mov dword ptr [ebp-10], edi
03A5254B 897D F4 mov dword ptr [ebp-C], edi
03A5254E 897D F8 mov dword ptr [ebp-8], edi
......
03A52565 6A 08 push 8
03A52567 57 push edi
03A52568 68 09040000 push 409
03A5256D 8D45 10 lea eax, dword ptr [ebp+10] ;参数
03A52570 50 push eax
03A52571 8D45 F0 lea eax, dword ptr [ebp-10] ;局部变量
03A52574 50 push eax
03A52575 FF15 5812A403 call dword ptr [<&OLEAUT32.#147>] ; OLEAUT32.VariantChangeTypeEx

用一个局部变量来作为pvargDest,而且这个目标Variant事先被初始化为vt=VT_EMPTY(0),这跟我前面29楼的分析是一致的,即它既不能是会导致相应对象被释放的,也不能是未初始化的
2010-3-16 17:24
0
雪    币: 70
活跃值: (74)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
34
嗯,这么定义pvargDest还是很合理的。。。这也是为什么IE7不打补丁的原因吧,IE8已经修复它了。。。
2010-3-16 17:29
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
35
由于这个重用机制的专用性,它只是通过CreateTearoffThunk和PlainRelease进行缓冲区的回收和再利用,分析发现它有两个缓冲区指针,当一块新的内存被释放时它被置换写入这两个指针位置,而被它置换出来的旧内存块(如果存在)才会被释放到堆中。因此只有当这块内存被放回缓冲区指针中被重用之前又有两块同样用于TearoffThunk的内存被释放并把它置换出来的情况下,PlainRelease才会调用HeapFree把原来这块内存释放到堆中。这意味着通常情况下这块内存被释放到堆中的可能性不大,而且时机比较难把握,实际情况基本上就是被CreateTearoffThunk重用的,相应的位置就只能被CreateTearoffThunk指定为某个地址。
但这个地址的可能性不一定是唯一的,首先是IE版本不同导致的影响;其次我们调试的过程中通过与调试器之间的切换过程其实也会改变浏览器的程序运行流程,这使得我们的调试过程本身对这个过程可能会产生影响;另外PoC的写法不同也会影响到。
在这种情况下攻击者不能够随意操纵这个EIP地址到任意地址,只有当这个地址是有很大可能落在能通过Heap Spray覆盖到的区域内的,才能通过Heap Spray有效地实现远程执行任意代码,如果刚好地址太过高或者刚好位于已被分配的内存中,就可能只能导致拒绝服务或其他不可预知的问题。
如果我上面的分析没有错,从这个方面上看,与之前的IE极光漏洞相比,这个漏洞的利用相对局限一点。
因为从我之前对IE极光漏洞的分析中(见http://bbs.pediy.com/showthread.php?t=105899),可以看到IE极光漏洞中对应的内存是通过RtlFreeHeap被释放回堆中,然后在堆中再分配的,这使得攻击者有可能把握住再分配的时机从而通过Heap Spray把这一块内存抢先重用并覆盖为自己想要的内容,从而通过这伪造的虚函数表指针控制EIP,实际上PoC也正是这么做的(覆盖为0x0a0a0a0a)。而相对而言,这个iepeers.dll的漏洞则做不到这样精确的控制。
2010-3-16 17:42
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
36
这个漏洞可以做到和aurora一样精确的控制eip, 具体可以参考这个文章:http://hi.baidu.com/inking26/blog/item/bc9a014f8ba1033daec3ab38.html
2010-3-18 13:13
0
雪    币: 722
活跃值: (123)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
37
是的,这跟我35楼说的是一致的,就是要在最早被释放的内存被重用之前再连续释放两个,使得最早的一个被挤出去释放到堆里。理论上是这样的,不过因为我没有见到有具体实现的代码,我对编写这种代码也实在不精通,所以不知道实现起来的难易程度如何。
另外,这篇文章置顶这么久,应该说已经起到了抛砖引玉的作用,这段时间来大家一起讨论这个问题,出现了不少有价值的回复,也帮助我逐步修正和深化了对这个问题的理解。
2010-3-19 00:25
0
雪    币: 216
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
38
太专业了,看不懂,继续学习!!!!
2010-3-19 09:31
0
雪    币: 181
活跃值: (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
39
http://www.exploit-db.com/
http://www.sebug.net/vuldb/
http://www.nsfocus.com/
2010-3-19 09:40
0
雪    币: 270
活跃值: (122)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
40
看起来有点晕,楼主厉害
2010-3-19 11:51
0
雪    币: 219
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
41
收藏了。学习中。
2010-3-19 12:02
0
雪    币: 234
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
42
分析的太好了,关键是过程,谢谢分享!不送你一贴都感觉过意不去。呵呵!!!
2010-3-20 09:53
0
雪    币: 261
活跃值: (83)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
43
"我连Heap Spray部分都去掉了"  这是什么意思?如何LZ是省略了这一步的话,那么请LZ补上,这样的话这个帖子就完善N多了!
2010-3-20 13:17
0
雪    币: 357
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
44
无话可说只有膜拜了.
2010-3-20 19:10
0
雪    币: 134
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
45
今天就看到分析了
2010-3-21 19:49
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
46
额 学习了,工作之余 逛逛论坛 呵呵
2010-3-22 13:46
0
雪    币: 201
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
47
呵呵,去微软看了一下,说这个漏洞是启明星辰的ADLAB团队找到的?八卦,纯属八卦~
2010-3-31 13:53
0
游客
登录 | 注册 方可回帖
返回
//