首页
社区
课程
招聘
[原创] 从补丁到Root——CVE-2014-4323分析
发表于: 2018-12-19 13:31 7630

[原创] 从补丁到Root——CVE-2014-4323分析

2018-12-19 13:31
7630

CVE-2014-4323漏洞详情

漏洞类型:CWE-264 Permissions, Privileges, and Access Control,一个权限提升漏洞。我们从补丁开始反推其利用。

这个漏洞 影响基于以下芯片组的所有基于Qualcomm的设备:
APQ 8064(Snapdragon S4 Pro)
MSM 8960(Snapdragon S4)
MSM 8660(Snapdragon S3)
MSM 8x30
MSM 7x30
因此,基于这些SoC的所有设备(例如Nexus 4,Nexus 7等),以及2014年12月之前的内核,应该是易受攻击。

刚好手上有个nexus 4的测试机,cat /proc/version查看内核版本,14年10月份,存在漏洞。

Linux内核3.x的MDP显示驱动程序中的drivers / video / msm / mdp.c中的mdp_lut_hw_update函数,用于Qualcomm Innovation Center(QuIC)Android对MSM设备和其他产品的贡献,不会验证某些启动和ioctl调用中的长度值,允许攻击者通过精心设计的应用程序获得权限提升。

补丁代码:确保来自用户态输入的cmap是合理的
图片描述

我们来看补丁检验的这个结构体 struct fb_cmap *cmap
图片描述

start是一个32的值但从注释来看是被做为指针的。
len是表示每个entry的个数。
r g b三色的值。

含有漏洞的代码,在for循环遍历entry这里for (i = 0; i < cmap->len; i++) 没检验cmap->len的长度的情况下,若cmap->len的值为1,执行后面的MDP_OUTP函数。会产生漏洞,此时i=0,而其他gbr 三色的值我们可控。

MDP_OUTP函数的定义为:

往内存映射的 I/O 空间上写数据,将 ((g & 0xff) |((b & 0xff) << 8) |((r & 0xff) << 16)))组成的数据写入到这个

地址上
图片描述
我们就拥有了一个可以在内核上可以在32位地址上任意写入6 X 4=24位值的漏洞!

要利用这个洞,前提我们得先知道MDP_BASE,mdp_lut_i这里两个变量的值。
MDP_BASE:是一个定义为常量内存映射地址的宏(每个SoC一个)

mdp_lut_i:是一个标志,在每次调用MDSSFB_SET_LUT时交替设置为0或1。这意味着0x400*mdp_lut_i的值为0或0x400。
我们通过尝试一次触发使用cmap-> start值覆盖漏洞,看其覆盖的位置推出mdp_lut_i的值。
图片描述

尝试往默认地址写入0x00ff0000

手机换成了红色:
图片描述

先确认我们的nexus 4机子是32位还是64位?
图片描述
armv7确认是32位,若是64位会有Aarch64等64标号(也可以读取Android 的system/build.prop文件("ro.product.cpu.abilist64"))。

根据含有漏洞的mdp_lut_hw_update函数调用链往上回溯:(之后我们会在内核崩溃日志的trace也看到这个调用链)
mdp_lut_hw_update-->mdp_lut_update_lcdc -->mdss_fb_set_lut-->mdss_fb_ioctl

mdss_fb_ioctl对应的就是这个设备的fb_ioctl操作。我们可以通过调用 ioctl(mdp_fd, MSMFB_SET_LUT, &cmap);函数来触发漏洞。

图片描述
内核空间划分0~3G为用户空间,3~4G为内核空间。
需求:
我们知道一般给的poc是导致内核崩溃,我们也制造一个内核panic的poc,那么如何制造内核崩溃呢?这篇文章给了三种方式。

1.Unable to handle kernel paging request at virtual address 00000000
越出内核地址空间范围,原因是由于使用空NULL指针

2.Unable to handle kernel paging request at virtual address 20100110

越出内核地址空间范围,原因是的内存越界导致该指针

3、Unable to handle kernel paging request at virtual address c074838c

没有越出内核地址 访问受限制内存导致oops

我们将0x00ff0000(红色的值),写入默认地址偏移为0x00ff0000的位置。手机panic重启了。

cat /proc/last_kmsg 查看上次最后的kernel log,寻找崩溃的原因:地址0xf0590800>0xc0000000属于往访问受限制内存导致的oops( Android-7.0 or above, the last_kmesg log is moved to: /sys/fs/pstore/console-ramoops

#cat /sys/fs/pstore/console-ramoops)

通过end trace我们也能看出mdp_lut_hw_update在内核里的调用链。
[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)
[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)

三种提权思路:

2.1 patch sys_setresuid然后调用setresuid提权
2.2 执行commit_creds(prepare_kernel_cred(0))提权
2.3 修改当前进程的task_struct->cred结构体进行提权

setresuid(0,0,0)可以用来设置进程的EUID,实现为当前进程提权的目的。但是普通用户直接调用并不能实现提取,原因如下:
图片描述

上图中对应了内核文件中setresuid的部分代码信息,通过分析可以发现,函数在真正进行setresuid之前会对调用者拥有的权限进行检查,如上图红框中的内容,满足调用权限时,R0的值为#0,对于普通用户的调用,R0是一个非零值,所以如果我们把比较的对象#0改成一个非零值,那么setresuid的可以成功调用进行置位。

CMP R0,#0 指令为0xe3500000
CMP R1,#1 指令为0xe3500001
arm的opcaode

所以我我们提权的步骤如下:

先读取/proc/kallsyms里的sys_setresuid的位置,利用任意写将其修改成0x3e500001,执行然后setresuid(0, 0, 0)即可完成提权。

图片描述

而由于我们只能控制后24位,无法写入0x3e500001到sys_setresuid的地址。此思路暂时不通。

图片描述

提权思路:覆盖内核里一个结构体方法的指针,将其地址指向用户态的代码commit_creds(prepare_kernel_cred(0)),最后再调用该结构体的方法完成提权。前提是我们知道commit_creds和 prepare_kernel_cred等函数地址。我们通过先patch掉kptr_restrict为我们构造能泄露内核函数地址的环境。

从内核2.6.37开始,普通shell用户没有办法从/proc/kallsyms中读到内核符号表地址。先patch掉kptr_restrict
我这里是直接在root下patch kptr_restrict
shell@mako:/ #echo 0 > /proc/sys/kernel/kptr_restrict

用此种方式无需硬编码,来适配不同的安卓/linux设备。

图片描述

我们这里是先找到“pppolac_proto_ops”结构中找到包含函数指针的位置。这是内核中用于注册与PPP_OLAC协议的套接字交互时使用的函数指针的结构。这种结构是合适的,因为:
1.PPP_OLAC协议没有被广泛使用,因此不需要立即恢复被覆盖的函数指针
2.除了创建套接字的能力之外,打开PPP_OLAC套接字不需要特殊权限
结构本身是静态的(因此存储在BSS中),并且没有标记为“const”,因此是可写的

因为我们只可控后24位,我们在0x00100000这个用户空间地址构造跳到payload的跳板trampoline,并将跳板trampoline的地址写到pppolac_proto_ops结构体的地址PPPOLAC_PROTO_OPS_RELEASE上。当我们执行socket. close()函数时jump到跳板里执行我们提权的payload。

这个利用代码借鉴的ggggwwww大佬的这篇文章让子弹继续飞-如何利用一个漏洞代码root更多手机 总体利用流程如下

其中有两点值得学习:
32位系统上sock信息泄露漏洞和修改task_struct的方式。

一:inet_diag信息泄漏问题:

当socket被关闭时destruct指针指向的函数将被执行。我们通过sock地址和destruct的偏移找到destruct函数指针的地址。

跑exploit的结果如下:这里我们发现已经修改过task_struct结构的进程的id,为何groups依然为1003(graphics)?

正常情况下sh和有root权限的init进程的cred结构值如下:
图片描述

利用代码

到此,本菜鸡终于完成了一次漏洞补丁分析到利用提权的过程。

https://android.googlesource.com/kernel/msm/+/65e9273c22264162c85351c5c29c94ff7ee2285e/drivers/video/msm/mdp.c

让子弹继续飞-如何利用一个漏洞代码root更多手机

Android系统漏洞提权

Android内核sys_setresuid() Patch提权CVE-2012-6422

Android内核漏洞利用技术实战:环境搭建&栈溢出实战

 
 
 
 
 
 
 
 
 
 
 
 

[峰会]看雪.第八届安全开发者峰会10月23日上海龙之梦大酒店举办!

最后于 2019-1-19 10:12 被endlif编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (8)
雪    币: 5633
活跃值: (7084)
能力值: ( LV15,RANK:531 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2018-12-19 16:02
1
雪    币: 4
活跃值: (516)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

2018-12-19 16:38
1
雪    币: 7048
活跃值: (3522)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
4
厉害。
2018-12-19 17:48
1
雪    币: 53
活跃值: (106)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
写得很详细,
2018-12-21 11:25
1
雪    币: 24478
活跃值: (62819)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
6
666
2018-12-24 15:45
0
雪    币: 2473
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
0x03 漏洞利用(未开启PXN)
先确认我们的nexus 4机子是32位还是63位?
2019-1-16 12:36
0
雪    币: 29
活跃值: (290)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
8
PYGame 0x03 漏洞利用(未开启PXN) 先确认我们的nexus 4机子是32位还是63位?
不知道你想表达的是啥?64位的机子是默认开启PXN的,我这里简单的通过判断是多少位,来判断是否开启PXN。
2019-1-19 09:21
0
雪    币: 29
活跃值: (290)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
PYGame 0x03 漏洞利用(未开启PXN) 先确认我们的nexus 4机子是32位还是63位?
哦哦 打错了 是64  已更正
2019-1-19 10:11
0
游客
登录 | 注册 方可回帖
返回
//