Background(背景)——off-by-one(大小差一)错误介绍
详细了解请移步这里 off-by-one(Off-by-one error),大小差一错误是一类常见的程序设计错误。这方面有一个经典的例子OpenSSH.去Google搜索关键词“OpenSSH off-by-one”可以了解相关状况。具体来说,
-
if(id < 0 || id > channels_alloc)…
-
if(id < 0 || id >= channels_alloc)…
第二句应该是正确的写法。举个更通俗的例子:
int a[5],i;
for(i = 1;i < = 5;i++)
a[i]=0;
上述代码定义了长度为5的数组a,循环的目的是给数组元素初始化,赋值为0.但是,循环下标从1开始到5,出现了a[5]=0,这样的不存在的数组元素.这就是典型的“差一错误”(off-by-one).
Introduction(前言)
我认为我决定介绍该漏洞的理由是因为当我把它发在推特上时,我收到了一些私信说这个内核路径不存在漏洞(找不到漏洞在哪)或者该漏洞不能利用。另一个的理由就是我想在实际漏洞利用中尝试userfaultfd() 系统调用,我需要一个真实的UAF漏洞来进行实验。
首先,我不知道这个漏洞影响到哪些Linux发行版本的内核。我检查了ubuntu的最新发行版本Yakkety,发现其没有受该漏洞影响。漏洞在Linux kernel source tree被引入,在Linux kernel source tree中被修复。
因为我需要一个含有该漏洞的ubuntu内核,我在ubuntu16.04(x86_64)上编译了4.5.1内核。另外该漏洞仅仅影响像ubuntu一样默认使用AppArmor作为LSM(Linux安全模块)的发行版本,像centos使用SELinux就不受该漏洞的影响。
Vulnerability(漏洞介绍)
当写入/proc/self/attr/current 会使用内核中的proc_pid_attr_write() 函数。接下来介绍的代码是漏洞被引入之前的:
static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = file_inode(file);
char *page;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
length = -ESRCH;
if (!task)
goto out_no_task;
if (count > PAGE_SIZE) [1]
count = PAGE_SIZE;
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out;
length = -ENOMEM;
page = (char*)__get_free_page(GFP_TEMPORARY); [2]
if (!page)
goto out;
length = -EFAULT;
if (copy_from_user(page, buf, count)) [3]
goto out_free;
/* Guard against adverse ptrace interaction */
length = mutex_lock_interruptible(&task-;>signal->cred_guard_mutex);
if (length < 0)
goto out_free;
length = security_setprocattr(task,
(char*)file->f_path.dentry->d_name.name,
(void*)page, count);
…
查看完整版请移步看雪论坛
[翻译]CVE-2016-6187 Exploiting Linux kernel heap off-by-one 利用堆大小差一错误爆破Linux内核(上)