回到首页| 网络安全 名人故事 申请书 | 名人名言 财富榜 关于我们

当前位置:名人故事传 > 互联网 > Linux服务器 > > 正文

初识linux内核漏洞利用

09-09  Linux服务器     来源: 未知  
0x00 简介

之前只接触过应用层的漏洞利用, 这次第一次接触到内核层次的,小结一下。

0x01 概况

这次接触到的,是吾爱破解挑战赛里的一个题,给了一个有问题的驱动程序,要求在ubuntu 14.04 32位系统环境下提权。驱动实现了write函数,但是write可以写0x5a0000000个字节。然后还实现了一个ioctl,这里有任意地址写的问题(但是这个分析里没用到)。还有一个read函数,这个可以读取堆上的数据。驱动的代码可以在这里下载到: www.52pojie.cn/thread-480792-1-1.html

#!cppstatic ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){    unsigned long p =  *ppos;    unsigned int count = size;    int ret = 0;    struct mem_dev *dev = filp->private_data;    if((dev->size >> 24 & 0xff) != 0x5a)     //dev->size == 0x5aXXXXXX        return -EFAULT;    if (p > dev->size)        return -ENOMEM;    if (count > dev->size - p)        count = dev->size - p;    if (copy_from_user((void *)(dev->data + p), buf, count)) {        ret =  -EFAULT;    } else {        *ppos += count;        ret = count;    }    return ret;}static long mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){    struct mem_init data;    if(!arg)        return -EINVAL;    if(copy_from_user(&data, (void *)arg, sizeof(data))) {        return -EFAULT;    }    if(data.len <= 0 || data.len >= 0x1000000)        return -EINVAL;    if(data.idx < 0)        return -EINVAL;    switch(cmd) {        case 0:            mem_devp[data.idx].size = 0x5a000000 | (data.len & 0xffffff);            mem_devp[data.idx].data = kmalloc(data.len, GFP_KERNEL);            printk(KERN_DEBUG "heap:%p\n",mem_devp[data.idx].data);            if(!mem_devp[data.idx].data) {                return -ENOMEM;            }            memset(mem_devp[data.idx].data, 0, data.len);            break;        default:            return -EINVAL;    }    return 0;}static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){    unsigned long p =  *ppos;    unsigned int count = size;    int ret = 0;    struct mem_dev *dev = filp->private_data;    if((dev->size >> 24 & 0xff) != 0x5a)        return -EFAULT;    if (p > dev->size)        return -ENOMEM;    if (count > dev->size - p)        count = dev->size - p;    if (copy_to_user(buf, (void*)(dev->data + p), count)) {        ret =  -EFAULT;    } else {        *ppos += count;        ret = count;    }    return ret;}

write里的dev->data是通过调用ioctl后kmalloc出来的,kmalloc的size可以自行指定。于是通过这个write,可以写内核堆,甚至写到内核栈里。我用的方法是覆盖内核某个堆结构,改掉其上的某个指针,最好是某个函数指针,或者函数表指针。具体的是shmid_kernel结构的file指针,里面存有shm_ops,这是shm的函数表,里面有shm_mmap,而这个函数可以在用户态通过shmat调用到。shmid_kernel这个结构体,则会通过在系统调用shmget时,被kmalloc。在我操作的机器上(32位):

shmid_kernel分配时的大小是64+92 = 156:

#!cppstruct shmid_kernel //结构体大小为92bytes{       struct kern_ipc_perm    shm_perm;    struct file     *shm_file;    unsigned long       shm_nattch;    unsigned long       shm_segsz;    time_t          shm_atim;    time_t          shm_dtim;    time_t          shm_ctim;    pid_t           shm_cprid;    pid_t           shm_lprid;    struct user_struct  *mlock_user;    struct task_struct  *shm_creator;    struct list_head    shm_clist;  };
0x02 覆盖前的堆排布

要保证能覆盖到特定的结构,首先是要保证,申请到的内存是相邻的。内核里kmalloc是slab的分配机制。一次至少会分配一个页面,然后把这个页面分为很多个连续的块,这些块的信息,可以通过 cat /proc/slabinfo 看到:

分配的时候,是向上对齐的。比如,如果kmalloc的size满足区间(128,192],那么就会给它分配一个192大小的块。如果有空闲的块,则把空闲的块分配出去。只有当所有分配的slab里的块,都被占用了,才会去分配新的slab(里面有很多相邻内存的大小相同的块)。比如说需要一个192的块,而已经分配的192的slab里没有空闲的,就会分配一个页面的内存,里面分成4096/192 = 21个192bytes的块,然后拿出第一块分配出去,再申请,则拿出第二块,以此类推。

//slab的图

所以,如果我们想要得到两个相邻的块。有这么几点要求:

申请的两个块的大小是处于同一区间的(这里假设都是申请192的块)申请之前得消耗掉所有空闲的大小为192的块两个块要连续申请。也就是申请第一个块之后要马上申请第二个。

所以,在这里来说,我们想要通过write,来覆盖掉下一个堆块,即我们的目标堆块shmid_kernel (占用一个192的slab块),要这么做:

不断调用ioctl(fd,0,&arg),并设置arg.idx = 192,来消耗掉空闲的192大小的slab块。

马上调用shmget(IPC_PRIVATE,1024,IPC_CREAT | 0666)来申请一块192的空间。这时,这个块有20/21的概率,我们最后一次ioctl得到的块,是相邻的。

#!cpparg.idx = 0;arg.len = 192;for(i=0;i<1000;i++)    ioctl(fd,0,&arg);shmid = shmget(IPC_PRIVATE,1024,IPC_CREAT | 0666);arg.idx = 1;ioctl(fd,0,&arg);

这之后再用write来进行覆盖,就能达到我们的目的。

0x03 overflow shmid_kernel

为了确保我们的堆排布好了,我给这个有漏洞的驱动,patch了一行代码,使得能够把每次kmalloc的地址打印出来:

而且在exp里,调用shmget之后,再一次调用ioctl来kmalloc一个192的块。那么得到的dmesg:

最后两次 ioctl,中间相隔了2个0xC0的大小,其中一个应该是shmid_kernel。那么还有一个是什么?通过调用驱动的read,读取这段堆上的内存,我发现:还有一个是shmid_kernel结构的shm_file,排布是这样的:

addrtype0xc04e43c0dev[0]->data
互联网 网络安全 申请书 创业资讯 创业故事明朝十六帝故事
© 2012-2022 名人故事传网版权所有 关于我们 | 版权声明 | 网站协议 | 友情申请 | 免责声明 | 网站地图 | 联系我们 | 广告服务