首页
社区
课程
招聘
[原创]CVE-2015-3636(pingpong root) android内核 UAF漏洞分析
发表于: 2018-7-30 17:11 11386

[原创]CVE-2015-3636(pingpong root) android内核 UAF漏洞分析

2018-7-30 17:11
11386

去年差不多这个时候就计划把这个漏洞给分析了,由于android没有经常搞,所以踩了很多坑,中间一度因为各种原因停滞放弃,最近遇到一个事情让我下定决心把它了结,也算是解决一个心病。过程会写详细一点,给和我一样的初学朋友提供点帮助。这个漏洞keen在blackhat上讲过[8],是一个很经典的android内核漏洞,也是第一个64bit root,还是很有学习价值的。分析android内核的漏洞需要自己下载android源代码和内核源代码,reverse patch,编译调试。吾爱破解有个比赛就是写这个漏洞的exploit,并且还提供了相应的环境[3],所以我偷了个懒,直接拿过来用就行了。exploit我在github上也直接找了一份现成的[11],经我测试可用。

其实很多文章都对漏洞原理描述很清楚了,为了文章完整性我再赘述一下。补丁[12]是在net/ipv4/ping.c的ping_unhash中加了一句sk_nulls_node_init(&sk->sk_nulls_node)。


这行代码其实就是把node->pprev设置成了NULL。

我们再看看keen给的POC。

把内核源代码下载下来看看。

当调用socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)创建socket再调用connect时,在内核中调用到了inet_dgram_connect。

如果sa_family == AF_UNSPEC会根据协议类型调用相应的disconnect routine,对于PROTO_ICMP来说是udp_disconnect。

这里注意sk_node和sk_nulls_node共用了一个union,两者的定义也十分类似,似乎有一点类型混淆的感觉。



这里Unable to handle kernel paging request at virtual address的地址是0x1360而不是0x200200,可能出题的人在这里修改了一下。我们在IDA里面看看。如果采取自己编译调试的方式是可以加载vmlinux符号文件的,这里我们就只能自己从机器上得到函数地址和名称然后加载到IDA中了。把Image拖到IDA64中,Process type选择ARM Little-endian [ARM]。


把ROM start address和Loading address设置为0xFFFFFFC000080000(32位系统就是0xC0008000)。Android 8.0中才为4.4及以后的内核引入了KASLR,很显然我们这里没有KASLR,这个值是固定的。


选择64-bit code。


这个时候IDA是什么也识别不出来的,因为Image文件并不是一个ELF,用binwalk看一下就会发现其实它组成还挺复杂的。我们接下来从运行的虚拟机中导出内核函数名称和地址。在ubuntu这样的发行版和android内核中有Kernel Address Display Restriction,所以先把它关掉。


写一个简单的脚本把这些函数名加载到IDA里面。

出来的函数列表里面只有ping_hash没有ping_unhash,我们把ping_hash的End address改成0xFFFFFFC000409614再在0xFFFFFFC000409614处create function处理一下就可以了。


我们可以看到crash处0xFFFFFFC000409644和前后的代码。

这三行代码对应源代码中的下面这三行。

所以进一步确认了漏洞成因和我们前面所分析的一样。如何让IDA分析Image讲的有点多了,主要参考了[1]和[4]。接下来还是回到正题,既然说这是一个UAF漏洞那么哪里UAF了呢?在hlist_nulls_del之后还有一个sock_put。

sock_put将sk的引用计数减1,并且判断其值是否为0,如果为0的话就free掉sk。可以想到最后一次connect进入本不该进入的if分支之后如果我们提前mmap了0x200200(这里是0x1360)就不会崩溃,接下来进入sock_put,引用计数变成0,sk被free掉,但是文件描述符还在用户空间,这就造成了UAF。

我们可以先测一下这个EXP。不过要注意的是必须用adb shell过去然后su shell才能继承root的权限得到建立socket的权限。测试发现这个EXP确实是可用的,下面就开始调试。


我调试时的命令如下。

下载下来发现有gdb-7.11和gdb-8.0.1两个文件夹,由于pwndbg和GEF等插件目前好像还不支持gdb 8.x,所以我们选择gdb-7.11。找到gdb-7.11/gdb目录下的remote.c文件,注释掉这两行。

在后面加上下面这几行。

编译安装。

终于开始调试了,不过还有一个小坑,我们应该用gef-remote -q localhost:1234也就是加上-q参数不然会报错,原因在这里[7]。接下来进入漏洞利用的部分。我们可以看到在main函数中整个漏洞触发漏洞的过程和POC中一样。



在内核中physmap在一个相对较高的地址,而SLAB通常在一个相对较低的地址,通过喷射其它的内核对象使得SLAB分配器在相对高的地址分配PING sock对象造成physmap和SLAB重叠,这个过程叫做lifting。这里的“其它的内核对象”直接用PING sock对象其实就可以。


然后释放掉用来做lifting的PING sock对象,和physmap重叠的那一部分则留做触发漏洞。那么怎样才能知道什么时候PING sock对象已经被physmap中的数据填充了可以停止喷射以及怎样找到已经被填充的PING sock对象呢?在physmap spray中进行了大量的mmap操作,并且将mapped_page+0x1D8处赋值为MAGIC_VALUE+physmap_spray_pages_count,接下来search_exploitable_socket的时候用ioctl一个一个去试。

这里的time是timespec结构体,会调用到sock_get_timestampns。

这个函数会返回sk->sk_stamp,在我们的环境中它在sock对象中的偏移正是0x1D8。


找到exp_sock之后因为它已经完全在我们的控制之中了,所以函数指针也是可控的,对其调用close函数就可以控制PC了。可以看到close是在inet_close中调用的。


找一下发现偏移是0x28,所以我们将payload+0x28设置为payload的地址,将payload开头设置为0xFFFFFFC00035D788让它跳到kernel_setsockopt。


addr_limit规定了特定线程的用户空间地址最大值,超过这个值的地址用户空间代码不能访问。所以把addr_limit改成0xffffffff就可以对内核为所欲为了。现在我们已经来到了kernel_setsockopt,应该怎么改addr_limit呢?当内核需要去使用系统调用的时候就要去掉地址空间的限制,一般的流程是(1)oldfs=get_fs(),(2)set_fs(KERNEL_DS),(3)set_fs(oldfs),如果能绕过set_fs(oldfs)的执行,内核空间将一直对用户态打开,这样就绕过了限制。

注意这里因为我们控制了X0所以BLR  X5跳过了STR  X20, [X19,#8]。



截一张mosec2016上360冰刃实验室讲的《Android Root利用技术漫谈:绕过PXN》[5]中的一张图帮助理解。


现在可以任意读写内核了,下一步是修改全局mmap_min_addr让我们能够在用户态mmap null地址。

最后于 2018-11-30 18:00 被houjingyi编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (8)
雪    币: 21
活跃值: (78)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
楼主分析太好,学习了。
2018-7-30 17:27
0
雪    币: 3136
活跃值: (97)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
3
楼主威武,一再放弃,找时间要参考该帖把3636也搞完....
2018-8-3 15:37
1
雪    币: 1
活跃值: (733)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习了
2018-8-3 17:47
0
雪    币: 5633
活跃值: (7089)
能力值: ( LV15,RANK:531 )
在线值:
发帖
回帖
粉丝
5
demoLin 楼主威武,一再放弃,找时间要参考该帖把3636也搞完....[em_5]
加油,坚持就会有收获
2018-8-4 00:01
0
雪    币: 1233
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习啦,好文章
2018-8-9 19:08
0
雪    币: 29
活跃值: (290)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
分析的超详细,感谢好文。
按照文章说的动手自己做了下,搭环境太麻烦。
出现两个问题:
1.好像exp不能直接用
11.https://github.com/4B5F5F4B/Exploits/tree/master/Linux/CVE-2015-3636
2.调试的命令貌似用不了


代码倒是理解了,就是环境配置太麻烦  只能慢慢搞了
最后于 2018-11-21 17:27 被endlif编辑 ,原因:
2018-11-21 16:22
0
雪    币: 104
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
谢谢版主
2019-9-5 16:29
0
雪    币: 104
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
请问这个exp是怎么生成的呢?github是一个jni文件,想做一下复现学习一下
2019-9-6 08:44
0
游客
登录 | 注册 方可回帖
返回
//