CVE-2015-3636

最近这今年,寻找一个android系统上可通用使用的root提权漏洞变得越来越困难,一方面是因为android系统的碎片化十分严重,另一方面是因为android系统上的漏洞缓冲机制在不断的引入。在这篇文章中我将为大家简单的讲述一下CVE-2015-3636这个漏洞的poc和exploit。其实要去稳定和有效的去利用这样一个漏洞并不是一件容易的事情,我们一步一步的来看。

0.漏洞概述:

漏洞属于linux内核级别的use-after-free漏洞,存在于Linux内核的Ping.c文件中。当用户尝试通过一个socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP)所返回的socket
file descriptor来调用两次一个sa_family==AF_UNSPEC的connect()时就会因为访问0x200200这个地址引起系统crash。除此,如果攻击者巧妙的填充或者覆盖PING socket对象,就能达到获取root权限的目的。并且,正是因为漏洞存在于android系统的基础部分Linux内核当中,才让这个漏洞有能通用利用的可能性。

1.漏洞分析&poc

这个漏洞是被keen team团队的Wen Xu和Wu shi发现并尝试利用的,目前已经被修补,CVE编号为:CVE-2015-3636。

我们从漏洞的补丁来分析漏洞:

我们从补丁源码可以看出,里面添加了sk_nulls_node_init(sk->nulls_node);这样一句代码。那么好,以这句话为切入点,结合前面概述中提到的poc思路分析漏洞原理。

通过浏览内核代码中关于sk_nulls_node_init(hlist_nulls_node *node)函数的具体实现后发现,其实这个函数的作用就是将参数所传入的的node的next成员和pprev成员赋为NULL。

概述中我们讲到的,利用socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP)所返回的socket file descriptor来调用两次一个sa_family==AF_UNSPEC的connect()时就会因为访问0x200200这个地址引起系统crash。接下来我们跟踪一下代码执行流程。

首先使用sa_family==AF_UNSPEC调用connect时会进入到inet_dgram_connect()函数中,

我们从补丁源码可以看出,里面添加了sk_nulls_node_init(sk->nulls_node);这样一句代码。那么好,以这句话为切入点,结合前面概述中提到的poc思路分析漏洞原理。

通过浏览内核代码中关于sk_nulls_node_init(hlist_nulls_node *n)函数的具体实现后发现,其实这个函数的作用就是将参数所传入的的hlist_null_node的next成员和pprev成员赋为NULL。

概述中我们讲到的,利用socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP)所返回的socket file descriptor来调用两次一个sa_family==AF_UNSPEC的connect()时就会因为访问0x200200这个地址引起系统crash。接下来我们跟踪一下代码执行流程。

首先使用sa_family==AF_UNSPEC调用connect时会进入到inet_dgram_connect()函数中,在函数中我们可以看到因为sa_family==AF_UNSPEC导致程序会执行红框中

的逻辑,而这条语句中最终调用了sk->sk_prot->disconnect(sk,flags)函数,sk_prot是sk对象的的一个成员,指向一个包含了确定数量函数指针的指针表,而具体的这些函数执行哪里取决于它的协议类型,这些协议包含TCP、UDP等。因此sk->sk_prot_disconnect(sk,flag)这条语句最终是调用的udp_disconnect(struct sock *sk,int  flag)这个函数:

>

并且在socket对象不绑定端口的情况下,会执行sk->sk_prot->unhash(sk)这条语句,同样的我们找到对应的函数:ping_unhash(struct  sock* sk)

此时我们看到,程序逻辑进入到了漏洞所在位置,经过分析我们关注点重点放在了41和42行这两句代码中,41句这句其实是将sk对象在其对应的内核hlist中删除。具体实现我们可以看一下:

其实就是将hlist_nulls_node类型的节点对应链表中删除,并且将n节点的前向二级指针pprev赋值为LIST_POISON2这个值,进一步我们在内核源码中搜索这个宏发现,其对应的值就是0x200200。

第一次调用sin_family== AF_UNSPEC connect时程序产生不会任何异常,而仅仅只是为了使这个sock对象sk的对应节点成员的pprev值被赋值为0x200200。

而第二次调用sin_family== AF_UNSPEC connect时才是希望真正的触发漏洞使系统crash。因为当第二次调用的时候程序逻辑按照之前的流程走到42行代码时会再次删除对应的节点,并且当执行到*pprev=next 这句时会导致系统crash。这是因为*pprev=next这条语句其实是对第一次节点的删除操作后LIST_POISON2这个指针解引用,让其为next,而在内核中,这个LIST_POISON2地址是不能被访问的,所以会引起系统crash。

其实在这里出了一点小问题,澄清一下,第二次尝试调用后并没有像我们想象的crash,这是因为要进入if(sk_hashed())逻辑中必须在两次调用sin_family== AF_UNSPEC connect()之前先使用sin_family==AF_INET 来调用一次connect(),这样才可以sk对象在内核中hashed(其实就是被加入到内核hlist中)。

综上所述,完整的poc(或者这个其实应该叫漏洞检测代码)如下:

#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <linux/netlink.h>
#include <linux/if.h>
#include <linux/filter.h>
#include <linux/if_pppox.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <linux/unix_diag.h>
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#define MMAP_BASE 0x200000
#define LIST_POISON 0x200200
#define MMAP_SIZE 0x200000
int checkIsVulnerable()
{
    void * magic = mmap((void *) MMAP_BASE, MMAP_SIZE,
       PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS,
       -1, 0);//向0x20000到0x40000这个虚拟内存地址映射,并且将这个地址段中所有值设为0
    memset(magic, 0, MMAP_SIZE);
    *((long *)(LIST_POISON)) = 0xfefefefe;//给0x200200这个虚拟内存地址中赋值为0xfefefefe;
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    connect(sock, (const struct sockaddr *) &sa, sizeof(sa));
//  第一次用AF_INET sin_family来connect是为了让sk(sock 对象)在内核中hashed
    sa.sin_family = AF_UNSPEC;
     connect(sock, (const struct sockaddr *) &sa, sizeof(sa));
     /*每次用AF_UNSPEC调用connect会触发inet_dgram_connect()中的sk->sk_prot->disconnect()逻辑
    其中disconnect的具体实现是根据协议类型而定的,PING(ICMP)socket的具体实现disconnect()是
    udp_disconnect()未绑定端口的情况下会触发sk->sk_prot->unhash(sk)逻辑*/
    connect(sock, (const struct sockaddr *) &sa, sizeof(sa));//如果漏洞存在,第二次调用就会触发这个漏洞
    if (*((long *)(LIST_POISON)) != 0xfefefefe){
       printf("Device is vulnerable\n");
       return 1;
    }else{
      printf("Device is not vulnerable\n");
      return 0;
    }
}

2.漏洞利用:

前面我们说到这是一个use-after-free漏洞,然而在poc中我们并没有看到哪里进行use-after-free了。我们仔细再来看看漏洞存在位置的代码:

我们来看看42行sock_put(sk);这句代码到底执行了怎样的操作:

其实就是将的sock对象sk的引用计数进行减一操作,并且判断其值是否为0,如果为0的话就释放掉sk这个对象的内存。也就说当我们第二次调用sin_family== AF_UNSPEC connect时会进入到if逻辑中的sock_put(struct sock*sk)函数当中去然后释放掉sk对象的内存。然而这个sk对象是我们在用户空间创建的,其文件描述符还在用户空间中也就是在我们的掌控之中,并且使用free来告知内核释放掉这个对象所对应的内存,但是并不会清除内存中的数据,这就是一个典型的use-after-free漏洞。

想要获取通过此漏洞过得root权限我们要完成的目标有:

1.覆盖use-after-free的Ping sock 对象的内容;

2.在用户空间中去使用已经free掉的Ping sock对象,尝试通过某种方式获得在内核中执行代码的能力;

3.进行拥有在用户态去调用内核代码的能力执行后,就可以修改对应的进程权限,提升为root权限。

先来看第一步,用我们可控的数据来覆盖use-after-free的Ping sock对象,也就是re-filling操作,也是最困难和最关键,关系到exploit能否稳定有效的运行成功,

我们知道,目前Linux内核采用的heap 管理机制是在分配内核对象的使用使用SLUB/SLAB分配器,不同大小的内核对象对应不同的SLABs,在这种情况下我们想要做的是类似

使用类型为A的内核对象去填充类型为B的内核对象,这在多进程的linux内核中来说几乎是不可能完成的事情,因为影响的因素太多,内存布局很容易被别的进程影响。

最后,一种比较好的选择是使用physmap(内核物理直接映射区),physmap是一块内核空间中一块为了提高系统性能的而设置的区域,这块区域允许直接将用户空间地址映射到内核空间中去。并且在内核空间中physmap区域和PING sock SLABs一个在比较高的地址,一个在比较低的地址,通过lifting可能产生重叠。如图:

我们看到,在进行了一定次数的lifting操作之后,和我们用户空间映射的区域发生重叠的也就是我们能够使用的Ping sock对象,位于内核空间中相对比较高的地址处。我们在映射的这块用户空间区域中填充相同的数据,以便之后我们判断是否已经有我们可控的Ping sock对象,并决定是否停止lifting,我们将这些数据成为Magic value。

当判断已经有我们可控的Ping sock对象之后,我们就停止进行lifting,并释放掉在lifting过程中创建的Ping sock对象,仅保留已经由我们控制的对象句柄。

首先来看看我们的Magic value的值到底是什么:

可以看到,其实是我们通过mmap映射的这块空间中填充的是以8字节为单位的值,其中前四字节填充当前的地址,而后四字节填充一个我们定义的一个32位值。

这是因为我们在判断是否已经产生重叠时,是通过ioctl(sockfd,SIOCGSTAMPNS,(struct timespec*))这个系统调用来判断的:

这个函数的执行结果是什么呢,通过源码我们可以分析得出,其实这个函数是将sockfd对应的socket对象的中sk_stamp成员的值返回用户空间中,而这个成员的值是对象创建时的时间戳。

试想想,当lifting一定次数后,现在我们手中的能控制的Ping sock对象中其实全是已经被我们填充了之前提到的8字节Magic value的,调用此函数后就会返回一个8字节的时间戳值,而这个值对应的前四个字节就是sk_stamp在Ping sock对象中的地址,后四个字节用来判断返回结果的正确性。

一旦我们判断已经有可控的PING sock对象在mmap映射的区域了,我们就停止lifting,并释放无用的PING sock对象。

此时我们就完成了覆盖的工作,也就是走完了第一步。

在得到一个用户空间可控的PING sock对象之后,很容易我们就可以通过分析Ping sock对象的内容来控制处于内核上下文的PC寄存器值。

首先来看一下Ping  sock对象的结构:

从中我们可以看出,一个Ping sock对象对应一个叫sk_prot的结构体指针,这个结构体中存放了一系列的函数指针,而整个对象中的数据都是可控的,并且存放这些函数指针的

地址我们根据其在Ping sock对象中的偏移是可以计算出来的,于是现在内核上下文中的一个PC寄存器就是我们可控的了。比如我们可以通过简单的close(sockfd)就可以控制程序流向至我们想要的函数地址。但是此时只能是从内核空间到用户空间函数的跳转。(因为虽然从内核可以向用户空间和内核空间任意跳转,但是我们没办法知道内核中某个函数的地址,其实我们最想知道的是commit_creds,prepare_kernel_cred的地址,因为传统的shellcode就是直接通过内核中这两个符号地址来进行的。)

那么我们该怎么做才能达到提升为root权限的目的呢?

这里用到的一种方法就是采用泄露内核栈顶指针sp的方法,来改写addr_limit的值解除限制,并且根据sp来推算thread_info的地址,然后根据thread_info推算task_struct的地址,因为task_struct结构存放着当前进程的所有信息,包括权限,于是就根据task_struct的结构来解析其中对应的权限相关成员,并修改其值为root权限进程应该有的值:

至此,我们利用CVE-2015-3636进行root提权的目的已经完成。

结果如下:

时间: 2024-10-06 21:00:40

CVE-2015-3636的相关文章

如何确定Ubuntu下是否对某个CVE打了补丁

????前些日子在月赛中,拿到了一台Ubuntu14.04的服务器,但并不是root权限,需要提权.我Google了一下,找到了CVE-2015-1318,CVE-2015-1328,CVE-2015-1338这些可以用来提权的CVE和POC.当我用CVE-2015-1328来提权时,并没有成功,我当时就想知道我这台服务器到底打没打上这个CVE的补丁呢? ????后来,我在网上查了下,稍微琢磨了下,就有了这个方法,可能不一定是最方便最好用的.我在Google直接搜索CVE-2015-1328,找

CVE-2015-1328(本地提权漏洞)

/* # Exploit Title: ofs.c - overlayfs local root in ubuntu # Date: 2015-06-15 # Exploit Author: rebel # Version: Ubuntu 12.04, 14.04, 14.10, 15.04 (Kernels before 2015-06-15) # Tested on: Ubuntu 12.04, 14.04, 14.10, 15.04 # CVE : CVE-2015-1328 (http:

CVE-2015-1328 Ubuntu 12.04, 14.04, 14.10, 15.04 overlayfs Local Root

catalog 0. 引言 1. Description 2. Effected Scope 3. Exploit Analysis 4. Principle Of Vulnerability 5. Patch Fix 0. 引言 新技术.高性能技术的不断发展,越来越提升了操作系统的能力,而近几年出现的虚拟化技术,包括overlayfs虚拟层叠文件系统技术,则为docker这样的虚拟化方案提供了越来越强大的技术支撑,但是也同时带来了很多的安全问题抛开传统的overflow溢出型漏洞不说,还有另一

阿尔红军我让我特我问题沃特尔行业

http://www.houzz.com/ideabooks/38419124/thumbs/2015.01.04 http://www.houzz.com/ideabooks/38419135/thumbs/2015.01.04 http://www.houzz.com/ideabooks/38419147/thumbs/2015.01.04 http://www.houzz.com/ideabooks/38419107/thumbs/2015.01.04 http://www.houzz.c

哪敢跟学长这么

不少人面庞上有不由得惊呼出声http://weibo.com/09.16/2015/p/1001603887569338240338http://weibo.com/09.16/2015/p/1001603887569338268443http://weibo.com/09.16/2015/p/1001603887569342462767http://weibo.com/09.16/2015/p/1001603887569342462769http://weibo.com/09.16/2015/

右手缓缓握拢而

火红烈日炸裂的一路冲杀进去吧http://weibo.com/2015/09/16/p/1001603887216807041204http://weibo.com/2015/09/16/p/1001603887216811186273http://weibo.com/2015/09/16/p/1001603887216811186277http://weibo.com/2015/09/16/p/1001603887216811235528http://weibo.com/2015/09/16/

叶轻灵苦笑一声

灵光刚刚收起时灵兵潮流尽数的牧尘准备不错http://weibo.com/2015.09.16/p/1001603887492385369004http://weibo.com/2015.09.16/p/1001603887492389563376http://weibo.com/2015.09.16/p/1001603887492393757752http://weibo.com/2015.09.16/p/1001603887492397975161http://weibo.com/2015.

百度是否会骄傲是罚款撒家乐福

http://www.ebay.com/cln/koyche_0293/-/167468813012/2015.02.10 http://www.ebay.com/cln/x_l0513/-/167468823012/2015.02.10 http://www.ebay.com/cln/lhu9368/-/167461855010/2015.02.10 http://www.ebay.com/cln/lonb759/-/167364514017/2015.02.10 http://www.eba

百度的说法矿石结晶发神经

http://www.ebay.com/cln/zhay285/cars/167540455015/2015.02.09 http://www.ebay.com/cln/j-nen73/cars/167540456015/2015.02.09 http://www.ebay.com/cln/jin.p82/cars/167427490013/2015.02.09 http://www.ebay.com/cln/x-x6813/cars/167540459015/2015.02.09 http:/

百度得换个房间是否可使肌肤

http://www.ebay.com/cln/fenxi76/cars/167208337017/2015.02.09 http://www.ebay.com/cln/becn195/cars/167283623014/2015.02.09 http://www.ebay.com/cln/sh.sho7/cars/167306609010/2015.02.09 http://www.ebay.com/cln/zhji215/cars/167208345017/2015.02.09 http:/