首页
社区
课程
招聘
[原创]CVE-2017-8890 深度分析
发表于: 2018-4-16 18:09 13228

[原创]CVE-2017-8890 深度分析

2018-4-16 18:09
13228

笔者作为初入安卓内核漏洞利用分析的从业者,自 CVE-2017-8890 这个漏洞被曝之后不久就开始研究其成因、原理、触发以及利用。至今遇到了无数问题,也解决了无数问题,于是记下这篇文章以做小结,一来对这段时间的工作学习做一个归纳总结,二来希望能对同样对安卓内核漏洞感兴趣的朋友有一些细节上的帮助。


    + 编号:CVE-2017-8890

    + 类型:double free

    + 位置:/net/ipv4/inet_connection_sock.c

    + 描述:

分析一个漏洞的原理,最直接的方法是从补丁入手。


CVE-2017-8890 的补丁如下所示:

可以看到这个补丁非常简单,只添加了一行代码,作用是将 inet_sk(newsk)->mc_list 置为 NULL。再结合漏洞类型为 double free,很容易得知应该是释放流程中对 mc_list 这个结构体的处理不当,导致了这个漏洞产生。那么这里比较关心的问题如下:

    + 这个对象是什么?

    + 对象在哪里产生?

    + 对象在哪里释放?

    + 为什么会产生漏洞?


通过源码分析和搜索引擎可以知道,mc_list 这个对象代表的是组播列表。其原型如下:

既然这个结构体是组播列表,那么很容易得知,当创建组播,加入组播的时候,很可能就会创建这个结构体。


通过前面的推测,加上内核源码分析,能够得出如下调用链,将会创建这个结构体。


用户态:


内核态:

知道了对象,创建流程,通过源码分析也能够找到释放的调用链。


用户态:


内核态:


PS:这里不得不说一下,我很佩服挖到这个漏洞的大佬,但是 【ADLab原创首发】“Phoenix Talon”in Linux Kernel —潜伏长达11年之久的内核漏洞 的两次对象释放写在不同的位置很误导人。之所以能够看到两次不同的释放,是因为 RCU 系统的存在,ip_mc_drop_socket 这个函数导致释放操作,真正的释放在 __rcu_reclaim。软中断是由于时钟中断导致,都是 RCU 释放流程中的正常操作。两次释放都在同一个点,都是由于用户态 close 导致的。

那么问题的关键来了,为什么会产生这个漏洞。


在搜集信息的过程中我们已经知道了这个漏洞是一个 double free,既然是 double free,那必然是一次创建,两次释放。但是从上面对象创建和释放的流程来看,并不能找到两次释放的点。


这个时候,漏洞描述中的一句话给出了分析方向:


在 accept 的时候,子对象会从父对象复制一份 mc_list。


但如果是复制的话,对象应该也会复制一份,那么对子对象和父对象都进行释放,应该是不会产生漏洞的。带着这样的疑问对源码进行了分析之后得到答案:


首先 mc_list 是 sock 对象的一个成员,并且是由 sock 中的指针所指向的对象


accept 的时候会把 sock 对象拷贝一份而不是 mc_list


由以上两点就能够解释漏洞为何产生了 —— 多个对象拥有了指向同一个对象的指针。


那么这个漏洞已经不是 double free 了,甚至可以是 X free,因为只要 accept 多次,那么就会有多个对象指向同一个 mc_list。

从上面的分析中我们已经得到了几个触发漏洞的关键点:

    + 如何创建对象

    + 如何释放对象

    + 为何产生漏洞


那么 POC 的构建已经是轻而易举了,伪代码如下:

这里需要注意的是 accept 要对应 connect。

因为利用涉及到太多的技术细节,所以这里主要讲思路,具体还是要自己去调试分析才能知道明白,要知道,只有在调试器下,才是没有秘密的。


目前,我们拥有的条件如下:

    + 能够触发漏洞

    + 知道这个漏洞是 double free


目的如下:

    + 成功利用漏洞,达到任意地址读写


而漏洞利用就是在拥有的条件之下,不断想方设法提升当前权限,以达到最终目的的过程。

    + 创建漏洞结构体

    + 第一次释放漏洞结构体


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-2-2 10:05 被admin编辑 ,原因: 图片本地化
收藏
免费 1
支持
分享
最新回复 (15)
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
大佬带带我
2018-4-16 18:15
0
雪    币: 438
活跃值: (228)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
顶起,其实可以细点的。
2018-4-16 21:01
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
4
感谢
2018-4-16 23:58
0
雪    币: 23
活跃值: (32)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
ID蝴蝶 顶起,其实可以细点的。[em_41]
利用确实一笔带过了==!涉及到太多技术细节不太好讲
2018-4-17 10:06
0
雪    币: 4366
活跃值: (353)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
6
感谢分析。
有两个问题想请教一下:
1. 存在一个问题:在 64 位机上存在 PXN 防护,需要通过 jop 的方式来绕过这个防护机制,而 jop 需要能够控制至少一个寄存器,但 rcu_head 中的 next 是作为一个地址传过来的,并不能通过寄存器控制,所以在 64 位机上并不能直接通过这种方式完全利用达到提权的目的。 
64位机器上需要通过jop来绕过PXN,jop需要至少控制一个寄存器。这个可以理解。但是这句话“但 rcu_head 中的 next 是作为一个地址传过来的,并不能通过寄存器控制” 是什么意思?博主似乎没有对__rcu_reclaim函数的说明,可否从源码的角度来补充说明一下?

2.当要释放一个 mc_list 的时候,rcu_head 会被链到一个释放链表中
释放mc_list的时候,在ip_mc_drop_socket中是调用 kfree_rcu 来释放的。 mc_list 中rcu_head的释放可以在 rcu_do_batch函数中的while循环中找到。 
但是这个rcu_head是如何链到一个链表里的呢?

补充:精华的地方比如jop,希望博主可以详细描述一下XD
最后于 2018-4-17 15:09 被心许雪编辑 ,原因:
2018-4-17 10:08
0
雪    币: 23
活跃值: (32)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
心许雪 感谢分析。有两个问题想请教一下:1. 存在一个问题:在 64 位机上存在 PXN 防护,需要通过&nb ...
这个释放流程是由内核中的  rcu  系统实现的,在  ip_mc_drop_socket  中会调用  kfree_rcu,然后通过  rcu  的流程来调用接下来的一系列函数。kree_rcu  这里调用的直接是地址而不是一个指针所以这个值并不可控。上链的操作的话是在  call_rcu  。最后  jop  的话,主体思路首先覆盖一个常用的内核函数以达到永久劫持  EIP,然后泄露内核栈找到  addlimit  修改这个值让任意地址可以读写,然后就已经绕过了  pxn  了。
2018-4-17 11:45
0
雪    币: 23
活跃值: (32)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
谢谢提出问题,很关键
2018-4-17 11:46
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
9
Yearthmain 利用确实一笔带过了==!涉及到太多技术细节不太好讲
其实可以单开一贴把能讲的都写写,无需关心写的乱不乱。。可能你写一点我们就少摸很多坑。
2018-4-17 14:00
0
雪    币: 23
活跃值: (32)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
sakura零 其实可以单开一贴把能讲的都写写,无需关心写的乱不乱。。可能你写一点我们就少摸很多坑。
后面再把利用细节整理整理吧,其实思路挺简单的
2018-4-17 14:02
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
11
Yearthmain 后面再把利用细节整理整理吧,其实思路挺简单的
嗯嗯,感谢
2018-4-17 14:05
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
用ipv6  的  mc_list进行spray  在nexus机器上是可以的  但是其他机器  比如华为  魅族是不可以的  mc_list的大小不一样 slab分配的不一样
最后于 2018-4-23 14:09 被XingLiu编辑 ,原因:
2018-4-23 14:06
0
雪    币: 23
活跃值: (32)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
XingLiu 用ipv6  的  mc_list进行spray  在nexus机器上是可以的  但是其他机器  比如华为&a ...
确实是这样的,并且  sendmsg  在这些手机上也会因为释放会把  slab  块连在链上导致无法触发漏洞后续流程
2018-5-4 17:21
0
雪    币: 236
活跃值: (26)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
参考第三个的链接好像错了,我猜貌似是这个链接:http://www.freebuf.com/articles/terminal/160041.html
2018-5-6 01:20
0
雪    币: 275
活跃值: (254)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
15
楼主的深度分析,感觉还没有那么深啊。控制func之后,做pxn的绕过,func的执行上下文可能根据内核配置、实际运行情况这些因素而不同,到底怎么达到任意地址读、写,这个还得做不少事吧?
2018-10-13 18:21
0
雪    币: 47
活跃值: (418)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
16
heapspray 控制 ip_mc_socklist 就行了,里面有个 rcu_head, 在这里就可以控制住 pc 寄存器, 绕过 pxn,不要使用 jop,太麻烦,直接使用 360 之前说的 setsockopt 的方式绕过,那样可以关闭掉 addr_limit, 就可以任意读写内核了;关于 heapspray,32 位机器上,用 ipv6 喷射很稳定,64 位部分机型 ip_mc_socklist 比 0x30 要大,可以采用 setxattr 喷射,但稳定性不好,sendmmsg 有最小值限制,应该是不可以在这个漏洞上做喷射的,还有,在 4.4 的内核上,rcu 的释放总是 kthread中,而不是自己的进程
2018-12-9 23:41
0
游客
登录 | 注册 方可回帖
返回
//