[译]CVE-2014-4014:Linux内核本地权限提升利用

介绍

我们知道,CLONE_NEWUSER名称空间在Linux 2.6.23版本中被引入,最终完成于Linux3.8(从3.8开始,非特权进程也可以创建用户名称空间)。它被用于用户和组ID号空间,即一个进程的用户和组ID可以变得无论是从内部还是外部,用户空间都不同。举个例子,一个普通(非特权)进程就可以创建一个uid为0的名称空间。

因此,一个用户名称空间内部的用户和组ID,到外部的用户和组ID间的映射是很有必要存在的。这种映射允许操作系统在当一个进程在用户名称空间内执行操作,影响到外部名称空间时执行适当的权限检查。如,文件系统访问。但是,许多的Linux文件系统对“user-namespace”的识别并不完整。

其问题就在于错误的使用inode_capable()来决定用户或组的功能。让我们看一看 inode_change_ok()函数使用 inode_capable()检查调用者是否有足够的权限来执行chown,chgrp和chmod操作:

在(2)这里,inode_capable()被称作CAP_FSETID。inode_capable()随后基于到外部名称空间的uid和gid的映射检查调用者是否被允许执行chmod操作:

但是,正如(3)所见的,只是检查了inode->i_uid(而不是inode->i_gid).这意味着如果我们作为一个非特权用户(名称空间以外),拥有一个gid为0的文件(会发生什么呢),我们可以设置setgid在该文件丢失inode->i_guid的检查。不过确实,我们首先需要自己的(即我们的uid)文件,这是因为inode_owner_or_capable()会在(1)处检查:

下面的poc演示了这个利用技术

PoC

这个例子,我将使用Ubuntu 14.04:

首先,我们假设我们(vnik)拥有一个gid为0的文件:

因此,gid就是root,没有setuid或者setgid设置。我们创建一个用户名称空间,将我们的用户映射到root。

上面的代码创建了一个用户名称空间(3),一个当前用户(外部名称空间)到uid 0(内部名称空间)的映射(4)。新的名称空间内的子进程随即设置setgid(5)在目标文件。因为没有检查kgid_has_mapping(ns,inode->i_gid)在inode_capable()。我们可以设置setgid与任意gid值(即使我们不属于这个组的外部)。

 

现在我们已经替换了test文件为我们的shell(保护gid),让我们来设置setgid:

 

我们有了egid=0。话不多说,是的,我们现在可以读取和写入文件(以前只有gid=0才能可读或可写),但是这也是无法直接root的。

结论

这类的内核版本因为不正确的使用inode_capable()来确定用户功能,所以导致一个非特权用户“可能”能够升级其特权到root。然而,假使足够好运获得一个gid=0(或者其它什么你的目标gid)的文件。你得到了egid=0。接下来呢?我们是不是就能通过特定的方式将权限提升到root呢。

【@Vitaly Nikolenko