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\n", base+len);
return 0;
}
zzy@ubuntu:~/Linux_prj/PoC$ ./integer_overflow
126
安全建議
用戶態
- 設置合適的設備訪問權限 (“/dev/graphics/fb0”)
- 配置SEAndroid 文件訪問策略
內核態
- 做好參數校驗(pfn和size),尤其考慮好整數溢出導致校驗邏輯繞過的問題
Android驅動中的remap_pfn_range()校驗漏洞(CVE-2013-2596)