能力值:
( LV2,RANK:10 )
|
-
-
26 楼
强悍,没的说的。。。
|
能力值:
( 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了
|
能力值:
( LV2,RANK:10 )
|
-
-
28 楼
学习一下 嘿嘿··
|
能力值:
( 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从而导致不可预知的问题。
|
能力值:
( LV2,RANK:10 )
|
-
-
30 楼
顶一下,学习了,谢谢楼主
|
能力值:
( LV2,RANK:10 )
|
-
-
31 楼
jmp 0x0f09f883,中的地址貌似在我机器上就是0xe8xxxxxx,不知道为什么地址这么高啊,无法覆盖到
|
能力值:
( 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
|
能力值:
( 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楼的分析是一致的,即它既不能是会导致相应对象被释放的,也不能是未初始化的。
|
能力值:
( LV4,RANK:50 )
|
-
-
34 楼
嗯,这么定义pvargDest还是很合理的。。。这也是为什么IE7不打补丁的原因吧,IE8已经修复它了。。。
|
能力值:
( 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的漏洞则做不到这样精确的控制。
|
能力值:
( LV2,RANK:10 )
|
-
-
36 楼
这个漏洞可以做到和aurora一样精确的控制eip, 具体可以参考这个文章:http://hi.baidu.com/inking26/blog/item/bc9a014f8ba1033daec3ab38.html
|
能力值:
( LV12,RANK:300 )
|
-
-
37 楼
是的,这跟我35楼说的是一致的,就是要在最早被释放的内存被重用之前再连续释放两个,使得最早的一个被挤出去释放到堆里。理论上是这样的,不过因为我没有见到有具体实现的代码,我对编写这种代码也实在不精通,所以不知道实现起来的难易程度如何。
另外,这篇文章置顶这么久,应该说已经起到了抛砖引玉的作用,这段时间来大家一起讨论这个问题,出现了不少有价值的回复,也帮助我逐步修正和深化了对这个问题的理解。
|
能力值:
( LV2,RANK:10 )
|
-
-
38 楼
太专业了,看不懂,继续学习!!!!
|
能力值:
( LV2,RANK:10 )
|
-
-
39 楼
http://www.exploit-db.com/
http://www.sebug.net/vuldb/
http://www.nsfocus.com/
|
能力值:
( LV3,RANK:30 )
|
-
-
40 楼
看起来有点晕,楼主厉害
|
能力值:
( LV2,RANK:10 )
|
-
-
41 楼
收藏了。学习中。
|
能力值:
( LV3,RANK:20 )
|
-
-
42 楼
分析的太好了,关键是过程,谢谢分享!不送你一贴都感觉过意不去。呵呵!!!
|
能力值:
( LV3,RANK:20 )
|
-
-
43 楼
"我连Heap Spray部分都去掉了" 这是什么意思?如何LZ是省略了这一步的话,那么请LZ补上,这样的话这个帖子就完善N多了!
|
能力值:
( LV2,RANK:10 )
|
-
-
44 楼
无话可说只有膜拜了.
|
能力值:
( LV2,RANK:10 )
|
-
-
45 楼
今天就看到分析了
|
能力值:
( LV2,RANK:10 )
|
-
-
46 楼
额 学习了,工作之余 逛逛论坛 呵呵
|
能力值:
( LV2,RANK:10 )
|
-
-
47 楼
呵呵,去微软看了一下,说这个漏洞是启明星辰的ADLAB团队找到的?八卦,纯属八卦~
|