Own your Android! Yet Another Universal Root CVE-2015-3636 (中文翻译) (1)

摘要

近几年来,由于linux内核漏洞极少,并且不同的供应商也在设备上做了防范措施,想要在安卓上找到一个可以广泛使用的root方法已经变得愈加困难。

在这篇文章里,我们将要展示我们的一种可以广泛应用的root方案。相关的漏洞是 CVE-2015-3636,一个典型的linux内核UAF(use-after-free)漏洞,我们将深入讨论这个漏洞。由于内核allocator(分配器)的分离分配,利用这样一个UAF漏洞是十分困难的。我们将会展示如何利用这个内核UAF漏洞在大多数市面上的安卓4.3即以上版本的设备上进行提权,其中包含世界上第一个64位的root示例。简短的说,我们将会展示一种通用的方式来利用linux内核的UAF漏洞,也就是说,对于所有厂商的产品都有效。所有现在的内核防范措施比如PXN都可以被绕过。更重要的是,我们独特的、没有被文档记录的针对内核UAF漏洞的攻击技术具有稳定和准确的特征。

漏洞分析

漏洞是由Keen Team的Wen Xu和wush通过自己定制的PC linux系统调用fuzzer工具Trinity发现的。我们将他移植到了android的ARM linux上。漏洞在最新的linux内核源码中已经被修复并且分配了CVE编号为CVE-2015-3636.

漏洞位于linux内核的base部分,这保证了它可以用于通用root。当有人试图通过已经由socket(AF_INET, SOCK_DGRAM, IP-PROTO_ICMP)创建的socket文件描述符来调用connect的时候,这一段内核代码将处理用户的请求。

代码
并且如果sa_family == AF_UNSPEC,那么内核将会调用由协议类型确定的disconnect过程。对于一个PING(ICMP)套接字来说,disconnect过程即如下
disconnect过程
我们可以看到对于一个PING(ICMP)套接字来说,内核最终会调用sk->prot->unhash(sk),也就是下图中的ping_unhash()。
ping_unhash
正如在源代码中所展示的一样,如果sock对象(ICMP sock)sk已经被hash了,那么它将会试图删除其在内核hlist当中存储的sk_nulls_node。如下图所示。
delete

我们可以想到在n(sk->sk_nulls_node)被删除以后,n->pprev将变为LIST_POISON2,而这个值是一个常数。实际上在android32位和64诶的内核里,这个值均为0x200200。这个虚拟地址可以被攻击者映射到用户空间。

然而,当第二次调用connect的时候将会发生一些神奇的事情。在socket对象从hlist当中删除后,它仍然是被hash的,因为它是否被hash是由sk->sk_node来决定的,而在第一次connection当中,没有被改变。

因此,内核将会进入if语句,然后会再次试图删除sk->sk_nulls_node。当内核执行了 *pprev = next,它将会crash,因为现在pprev的值为0x200200,而如果这个虚拟地址没有被映射进用户空间,那么一个严重的页错误将会在内核中发生,并且导致严重的后果。0x200200应该在第二次ICMP套接字连接之前被映射进用户空间来避免crash。然而,这样的一个本地DOS(拒绝服务)还不是这个漏洞的全貌。

简单看一看下图中的代码
figure 5
在hlist_nulss_del被调用之后,我们可以发现sock_put(sk)非常值得怀疑。

每当内核进入if语句的时候,它都会为内核中sock对象的引用计数减一。而最重要的是,它会检查引用计数是否为0。如果为0,sock对象将会被free掉。这意味着如果有人试图第二次connect这样一个sock对象的时候,引用计数将会变为0,然后内核就会释放(free)它。但是用户程序中的文件描述符依然联系在对应的内核中的sock对象上,这导致了典型的UAF漏洞。

POC(Proof-of-Concept)

这是一段CVE-2015-3636的PoC。

int sockfd = sock(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
struct sockaddr addr 
    = { .sa_family = AF_INET};
int ret = connect(sockfd, &addr, sizeof(addr));
struct sockaddr _addr 
    = { .sa_family  = AF_UNSPEC};
ret = connect(sockfd, &_addr, sizeof(_addr))
ret = connect(sockfd, &_addr, sizeof(_addr));

connect必须用AF_INET、sa_family首先调用来创建一个sk(那个有漏洞sock对象),在内核中被hash,否则那个if将根本不会到达。

注意一下,这个POC知在android设备上有效。允许创建PING套接字的group id范围在/proc/sys/net/ipv4/ping_group_rang中被设置。在android设备上,默认一个普通用户就由权利创建PING套接字,而在PC linux上,正常情况下没有任何人有权利创建它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值