zoukankan      html  css  js  c++  java
  • Android驱动中的remap_pfn_range()校验漏洞(CVE-2013-2596)

    简单介绍

    当然类似函数还有io_remap_pfn_range()。

    remap_pfn_range() 为用户态提供了一种手段访问内核地址空间。它通过新页表,将一块内核物理内存映射到用户态进程空间。

    remap_pfn_range() 函数的原型如下:

    int remap_pfn_range(struct vm_area_struct *vma, 
            unsigned long virt_addr, 
            unsigned long pfn, 
            unsigned long size, 
            pgprot_t prot); 
    

    其中

    unsigned long pfn 表示映射的物理起始地址

    unsigned long size 表示映射的内存大小

    remap_pfn_range() 函数内部没有对这两个参数进行控制。可以想象,当pfn传入内核态物理起始地址(0xc0000000),size传入内核空间大小(1G),便可以将整个内核映射到用户态,任意访问修改。

    在一种常用提权方式中,便可以利用这种能力将fsync()地址修改为shellcode地址,实现提权。

    因此,在编写驱动mmap接口代码时,***一定要准确的校验remap_pfn_range()的pfn和size参数。***

    一个CVE案例

    android_rooting_tools项目中,包含了一个这样的实例。libfb_mem_exploit工程包含了CVE-2013-2596的漏洞利用代码,这是高通图形设备驱动中的一个漏洞,由于整数溢出,导致绕过了remap_pfn_range()参数校验逻辑。

    下面代码注释位置,是漏洞根源:

    static int
    fb_mmap(struct file *file, struct vm_area_struct * vma)
    {
    	struct fb_info *info = file_fb_info(file);
    	struct fb_ops *fb;
    	unsigned long off;
    	unsigned long start;
    	u32 len;
    
    	if (!info)
    		return -ENODEV;
    	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    		return -EINVAL;
    	off = vma->vm_pgoff << PAGE_SHIFT;
    	fb = info->fbops;
    	if (!fb)
    		return -ENODEV;
    	mutex_lock(&info->mm_lock);
    	if (fb->fb_mmap) {
    		int res;
    		res = fb->fb_mmap(info, vma);
    		mutex_unlock(&info->mm_lock);
    		return res;
    	}
    
    	/* frame buffer memory */
    	start = info->fix.smem_start;
    	len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    	if (off >= len) {
    		/* memory mapped io */
    		off -= len;
    		if (info->var.accel_flags) {
    			mutex_unlock(&info->mm_lock);
    			return -EINVAL;
    		}
    		start = info->fix.mmio_start;
    		len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    	}
    	mutex_unlock(&info->mm_lock);
    	start &= PAGE_MASK;
    	
    /* 同时校验pfn与size参数,整数溢出将导致校验绕过 */
    	if ((vma->vm_end - vma->vm_start + off) > len)
    		return -EINVAL;
    	off += start;
    	vma->vm_pgoff = off >> PAGE_SHIFT;
    	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by io_remap_pfn_range()*/
    	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
    	fb_pgprotect(file, vma, off);
    	
    	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
    		return -EAGAIN;
    	return 0;
    }
    

    用户态mmap调用与fb_mmap的参数关系如下:

    prot——vma->vma_page_prot
    offset——vma->vma_pgoff
    length——vma->end - vma->start
    

    分析构造的PoC:

    mapped_address = mmap((void *)MAPPED_BASE, (0x100000000 - kernel_phys_address),
                        PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED,
                        *fd, kernel_phys_address + info.smem_len);
    

    以下是漏洞代码的校验逻辑:

    start = info->fix.smem_start;
    off = vma->vm_pgoff << PAGE_SHIFT;
    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    
    if ((vma->vm_end - vma->vm_start + off) > len)
    	return -EINVAL;
    

    vma->vm_end - vma->vm_start + off = (0x100000000 - kernel_phys_address) + (kernel_phys_address + info.smem_len) = 0x100000000 + info.smem_len

    由于整数溢出,0x100000000 + info.smem_len = info.smem_len > len 恒不成立,即绕过参数校验。

    下面代码直观感受整数溢出的效果:

    zzy@ubuntu:~/Linux_prj/PoC$ cat integer_overflow.c 
    #include <stdio.h>
    #include <stdlib.h>
    
    int main ()
    {
            unsigned long len = 126;
            unsigned long base = 0x100000000;
    
            printf ("%lu
    ", base+len);
            return 0;
    }
    zzy@ubuntu:~/Linux_prj/PoC$ ./integer_overflow 
    126
    

    安全建议

    用户态

    1. 设置合适的设备访问权限 (“/dev/graphics/fb0”)
    2. 配置SEAndroid 文件访问策略

    内核态

    1. 做好参数校验(pfn和size),尤其考虑好整数溢出导致校验逻辑绕过的问题
  • 相关阅读:
    javascript数组对象
    jquery、javascript实现(get、post两种方式)跨域解决方法
    js检查字符串的包含关系
    element——message消息提示
    js把mysql传过来的时间格式化为:0000-00-00 00:00:00
    beego orm操蛋问题:操作都需要主键
    element——message-box
    VUE.js全局变量的定义
    element el-upload组件获取文件名
    go遍历某个文件夹
  • 原文地址:https://www.cnblogs.com/gm-201705/p/9863998.html
Copyright © 2011-2022 走看看