0x00 CVE-2013-6123分析
0x01 漏洞描述
CVE-2013-6123是MSM摄像头驱动的漏洞,在MSM摄像头驱动中的msm_cam_server.c文件中的‘msm_ctrl_cmd_done’,‘msm_ioctl_server’及‘msm_server_send_ctrl’函数存在数组索引错误漏洞。攻击者可通过摄像头设备节点的访问权限利用该漏洞获取特权。该漏洞的主要是存在数组下标越界,使得用户端可以利用该漏洞突破内核地址防护,实现任意地址写。
0x02 漏洞补丁
补丁代码
在msm_ioctl_server这个方法里面加了一句关于这个值得校验
u_isp_event.isp_data.ctrl.queue_idx
poc的思路就简单了,我们就看越过这个值得范围会数组越界异常
0x03 代码分析
static int msm_ctrl_cmd_done(void *arg)
{
void __user *uptr;
struct msm_queue_cmd *qcmd;
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
struct msm_ctrl_cmd *command;
D("%s\n", __func__);
command = kzalloc(sizeof(struct msm_ctrl_cmd), GFP_KERNEL);
if (!command) {
pr_err("%s Insufficient memory. return", __func__);
goto command_alloc_fail;
}
qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
if (!qcmd) {
pr_err("%s Insufficient memory. return", __func__);
goto qcmd_alloc_fail;
}
mutex_lock(&g_server_dev.server_queue_lock);
if (copy_from_user(command, (void __user *)ioctl_ptr->ioctl_ptr,
sizeof(struct msm_ctrl_cmd))) {
pr_err("%s: copy_from_user failed, size=%d\n",
__func__, sizeof(struct msm_ctrl_cmd));
goto ctrl_cmd_done_error;
}
if (!g_server_dev.server_queue[command->queue_idx].queue_active) {
pr_err("%s: Invalid queue\n", __func__);
goto ctrl_cmd_done_error;
}
D("%s qid %d evtid %d %d\n", __func__, command->queue_idx,
command->evt_id,
g_server_dev.server_queue[command->queue_idx].evt_id);
if (command->evt_id !=
g_server_dev.server_queue[command->queue_idx].evt_id) {
pr_err("%s Invalid event id from userspace cmd id %d %d qid %d\n",
__func__, command->evt_id,
g_server_dev.server_queue[command->queue_idx].evt_id,
command->queue_idx);
goto ctrl_cmd_done_error;
}
atomic_set(&qcmd->on_heap, 1);
uptr = command->value;//1.command->value的值可控
qcmd->command = command;
//2.queue_idx的值,用户态可控,而且没有下标检查
//ctrl_data的值,用户态可控 而且内核可访问
if (command->length > 0) {
command->value =
g_server_dev.server_queue[command->queue_idx].ctrl_data;
if (command->length > max_control_command_size) {
pr_err("%s: user data %d is too big (max %d)\n",
__func__, command->length,
max_control_command_size);
goto ctrl_cmd_done_error;
}
//3.command->length用户态可控,任意地址写
if (copy_from_user(command->value, uptr, command->length)) {
pr_err("%s: copy_from_user failed, size=%d\n",
__func__, sizeof(struct msm_ctrl_cmd));
goto ctrl_cmd_done_error;
}
}
msm_enqueue(&g_server_dev.server_queue
[command->queue_idx].ctrl_q, &qcmd->list_control);
mutex_unlock(&g_server_dev.server_queue_lock);
return 0;
ctrl_cmd_done_error:
mutex_unlock(&g_server_dev.server_queue_lock);
free_qcmd(qcmd);
qcmd_alloc_fail:
kfree(command);
command_alloc_fail:
return -EINVAL;
}
我们已经拥有了一个任意地址写的漏洞:
此时的提权思路:
权限获取的几种方式
1.通过commit_creds(prepare_kernel_cred(0)方式。
这种漏洞方式的一般方法是通过patch ptmx_fsync,然后通过访问ptmx设备的fsync来触发。
fd = open(ptmx_device), O_WRONLY);
ret = fsync(fd);
这个的前提是我们可以获得ptmx_fync的地址。在获取不到ptmx_fync的时侯。
我们也可以通过patch相关的系统调用来实现。在本文中是利用patch settimeofday的方式,然后通过调用settimeofday函数来触发。
2.通过 patch uid、gid、capability、selinux的状态位。
这种方式是在漏洞利用不方便的时侯,随着安卓漏洞缓解技术的发展,这种方式越来越普遍。
0x04漏洞利用流程
获得root的步骤如下:
调用get_symbols_from_kernel,获取设备的地址。
通过驱动的任意写漏洞,通过patch_sys_settimeofday函数,将settimeofday的系统调用改成commit_creds(prepare_kernel_cred(0)的实现。
调用trigger_syscall_hook函数,使得获取ROOT权限的commit_creds(prepare_kernel_cred(0)代码得以运行。
fix_syscall_hook()将settimeofday的系统调用改成空实现,防止其他进程调用settimeofday而使系统崩溃。
0x05总结:
1.解包流程
得到rom包后,通过bootimg.py或者是bootimg.exe解压rom包,得到kernel。
从github中(https://github.com/fi01/kallsymsprint)下载kallsymsprint,运行以下命令得到
adb push kernel /local/tmp/kernel
adb push kallsymsprint /local/tmp/kallsymsprint
adb chmod 777 /local/tmp/kallsymsprint
adb shell . /local/tmp/kallsymsprint kernel > kallsyms.txt
https://github.com/fi01/kallsymsprint
2.编译我们自己的内核进行调试
启动模拟器
emulator -verbose -show-kernel -kernel
/Users/goldfish/arch/arm/boot/zImage -avd avd_test
exploit代码编译及运行
cd run_cve
ndk-build
adb push ./obj/local/armeabi/run_cve /data/local/tmp/
adb shell chmod 777 /data/local/tmp/run_cve