模糊测试syzkaller项目之Charm
Charm:促进动态分析移动系统的设备驱动
此文只是对原文的简要提炼,具体内容还请阅读https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-talebi.pdf
注: 本文与syzkaller的相关性较小,但涉及到对syzkaller的运用,可作为对syzkaller运用领域的拓展了解
Charm简介
- 一种便于动态分析移动系统设备驱动程序的系统解决方案
- 核心技术: 远程设备驱动执行.
- 目的: 使设备驱动程序能够在工作站上的虚拟机中执行.Charm通过使用实际的移动系统, 低延迟和定制的USB通道为低级别且不频繁的I / O操作提供服务,从而实现了这一目标.
- 益处: 由于设备驱动程序在虚拟机中执行,因此它使分析人员能够使用各种动态分析,包括手动交互式调试,记录和重放以及增强的模糊测试.(其中模糊测试工具使用了syzkaller)
核心技术简介
- 设备驱动程序与其I / O设备交互的尝试被虚拟机管理程序在虚拟机中拦截,并且通过专门的低速延迟USB通道发送到实际的移动系统。 在该技术中,虽然执行不频繁的低级I / O操作需要实际的移动系统,但是设备驱动程序在虚拟机内完全运行,因此可以进行分析。 Figure 1显示了Charm背后的高级理念.
- 其中定制的USB通道解决了因通信能力导致而I / O设备或驱动程序的各种超时问题.
- 同时作者团队构建了RemmoteProcedureCall(RPC)接口快速驱动器来与移动系统的原生接口进行交互,从而使不同移动系统的不同设备可以使用相同的RPC接口,从而减少了将Charm应用于新设备驱动程序的工程量。
关于虚拟机和底层驱动的一些交互方法
- 稻草人方法: 在移动系统中的虚拟机中运行设备驱动程序,并使用直接设备分配技术使虚拟机能够访问 底层I / O设备
- Charm方法: 简介见前
远程设备驱动程序执行的细节
- 设备和设备驱动程序交互:为实现此目的,工作站管理程序中的存根模块通过子模块进行通信,以支持设备驱动程序与其硬件的交互。 这些交互是三重的:访问I / O设备的寄存器,中断和直接内存访问(DMA).Charm目前支持前两个.
- 寄存器访问
- 中断
- 设备驱动程序初始化: 首先允许内核基于ACPI的设备检测. 之后,内核解析设备树以检测远程I / O设备. 这减少了工程工作量. 为了支持新设备驱动程序的初始化,我们只需要将与感兴趣的I / O设备相对应的设备树条目从移动系统的设备树复制到虚拟机的设备树条目.
- 低延迟USB通道: 在此频道中,我们为Charm创建了一个USB小工具接口[13],并将五个端点连接到此接口. 两个端点用于寄存器访问的双向通信。 两个端点用于RPC调用的双向通信(在第4.4节中解释). 最后一个端点用于中断的单向通信(从移动系统到工作站).
- 相关依赖
- 将设备驱动程序移植到Charm(建议此步看原文文档)
漏洞发掘
- Charm不仅有助于模糊测试,还可以实现移动系统内核当前不支持的模糊器的更新功能
- (a) Vulnerable code snippet of CVE-2016-3903
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/* in msm_csid_cmd(): */
1 for (i = 0; i < csid_params.lut_params.num_cid; i++) {
...
2 if (copy_from_user(vc_cfg, (void *)
csid_params.lut_params.vc_cfg[i], sizeof(struct msm_camera_csid_vc_cfg))) {
...
3 for (i--; i >= 0; i--)
4 kfree(csid_params.lut_params.vc_cfg[i]);
5 rc = -EFAULT;
6 break;
7 }
8 csid_params.lut_params.vc_cfg[i] = vc_cfg;
9 }
...
10 rc = msm_csid_config(csid_dev, &csid_params);
/* in msm_csid_cid_lut(): */
...
11 if (csid_lut_params->vc_cfg[i]->cid >=
csid_lut_params->num_cid ||
csid_lut_params->vc_cfg[i]->cid < 0) {
...
12 }
- (b) Vulnerable code snippet of CVE-2016-2501
1
2
3
4
5
6
7
8
91 int16_t step_index = 0;
2 uint16_t step_boundary = 0;
...
3 for (; step_index <= step_boundary; step_index++) {
...
4 if (cur_code < max_code_size)
5 a_ctrl->step_position_table[step_index] = cur_code;
...
6 }
- (c) Vulnerable code snippet of CVE-2016-2061
- 举一些例子
1
2
3
4
5
6
7
8
9
10
11
12
131 int i = stream_cfg_cmd->stream_src;
2 if (i >= VFE_AXI_SRC_MAX) {
...
3 return -EINVAL;
4 }
...
5 memset(&axi_data->stream_info[i], 0, sizeof(struct
msm_vfe_axi_stream));
...
6 axi_data->stream_info[i].session_id =
stream_cfg_cmd->session_id;
7 axi_data->stream_info[i].stream_id =
stream_cfg_cmd->stream_id;
CVE-2016-3903: 崩溃点位于第11行(在函数msmcsidcidlut()中),乍一看,这似乎是一个越界访问错误,但我们的调查(下面描述)显示这是一个免费使用后的错误。我们按如下方式进行了调查。通过使用观察点,我们发现崩溃站点的索引变量i始终在正常范围内(而不是负值).然后我们尝试检查其他指针值与GDB崩溃的网站并最终确定vccfg [i]持有无效地址.为了追踪数组vccfg的原点,我们利用观察点来跟踪其父结构csid_lut_params并最终找到另一个函数msmcsidcmd,它负责初始化结构.通过单步执行初始化代码,我们发现如果在第2行的vccfg初始化期间发生错误,它将在第4行释放,然后初始化循环将在第6行终止.但是,函数调用在行10将继续使用csidparams结构,无论其vc_cfg子字段已被释放,从而导致免费使用后漏洞.
CVE-2016-2501: 崩溃点位于第5行。当触发崩溃点的断点时,我们可以推断它可能是一个越界的阵列访问。 接下来,我们为索引变量步骤索引设置一个观察点,跟踪其值的变化。 实际上,当崩溃发生时,它的价值是负的。 仔细观察,作为循环索引,将其与第3行的步边界进行比较,第3行是保持0xffff值的16位寄存器。 但是,步骤索引是有符号整数,可以在达到0xffff之前取负值以终止循环(请注意,比较是无符号的)。 因此,当它在第5行用作数组索引时,会发生越界访问。 最后,我们还为步边界设置了一个观察点,并发现它的值来自用户空间传递的函数参数,这是不可信的。
CVE-2016-2061: 第一眼看到崩溃网站表明第5行的memset()可能会将无效的内存区域归零,从而导致内核崩溃。 实际上,通过检查崩溃站点上的各种变量值,我们发现我将负值作为数组索引,导致越界访问。 为了完全理解为什么我可能是负面的,我们在观察点的帮助下追溯它,并发现i的值来自用户控制的参数(line1)。 此外,不幸的是,第2行的完整性检查不能过滤负面i。 然后我们发现这是一个关键的漏洞。 这是因为从第6行开始,赋值语句的右侧也由源自用户空间的参数流cfg cmd控制。 与用户控制的索引变量i一起,此漏洞成为特权升级的理想目标,我们展示了我们可以实现的目标。
具体的exploit开发方法及其他的相关展望请参看原文
结语: 在可行性,表现和记录与重放性能上,建议读者根据具体情况对实验的相关数据进行参考.