您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件测试技术 > 其他相关 > 正文

现有 USB 模糊测试技术的总结(下)

发表于:2019-09-05 作者:xiaohui 来源:嘶吼
上文我们介绍了 USB 协议栈和 USB 模糊测试的历史,其中讲到了一些过去的常用技术和工具,不过它们都存在着一些问题。今天,我们将介绍最新的 5 种 USB 模糊测试的解决方案。

Syzkaller

不过幸运的是,最近安德烈科诺瓦洛夫(Andrey Konovalov)在 Google 上添加了 Syzkaller USB 模糊测试支持,并证明了它能够发现更多漏洞。 Andrey 解决了使用 Syzkaller 对 USB 进行模糊测试时的两个主要问题:

1. 内核任务的代码覆盖率;

2. 同一内核映像中的设备模拟。

由于 USB 事件和操作发生在 IRQ 或内核上下文,而不是进程上下文中(例如,旧内核中的 khub 内核任务中的 USB 插入检测),因此基于系统调用的跟踪和代码覆盖不起作用。为了能够在内核中的任何位置报告代码覆盖率,我们需要使用扩展的 KCOV 内核 API 来注释与 USB 相关的内核源代码(例如 hub.c),以报告代码覆盖率。不过,Syzkaller 不使用 QEMU,而是使用 gadgetfs 将模糊测试工具的内核驱动程序暴露给用户空间,然后用户空间可以管理输入内容以进行模糊测试。通过在内核配置中启用 USB 主机协议栈和 USB 从机协议栈,并使用虚拟 HCD 和 UDC 驱动程序将它们连接在一起。如下所示,Syakaller 能够模糊 USB 主机设备驱动程序,如 USB HID,海量存储等通过用户空间对 USB 模糊测试工具的内核驱动程序进行模拟。

 


Syzkaller USB 模糊测试工具可能是第一个真正的基于覆盖的 USB 主机设备驱动模糊测试工具,不过这还要归功于现有的 Syzkaller 基础设施以及对 USB 主机和从机发起的黑客技术。目前 Syzkaller 已经被发现了大量的漏洞和错误,它的局限性开始显现,目前发现的大多数问题都在驱动程序的初始化阶段(例如探测)。在用户空间中,模糊测试工具能够通过探索 USB 从机描述符中的不同 VID / PID 组合,来配置模糊测试工具内核驱动程序,进而表示任何 USB 从机。一方面,Syzkaller 能够触发几乎所有 USB 主机设备驱动程序进行加载,因此代码覆盖功能特别强大。另一方面,由于在用户空间模糊测试工具或模糊测试工具内核驱动程序内没有提供用于特定设备的真实模拟代码,因此大多数模糊测试在驱动程序初始化之后停止,因此仅覆盖驱动程序的一小部分。

 

最新的 USB 模糊测试技术

如上所述,大家可能已经注意到,所有这些模糊测试解决方案都集中在 USB 主机协议栈上,尤其是 USB 主机设备驱动程序上。另外,由于人们经常将 USB 引用到 USB 主机协议栈,并且这些设备驱动程序因包含比内核中的其他组件(例如 Windows 上的设备驱动程序)更多的漏洞而闻名。但是,以上讲的所有 USB 模糊测试工具所涵盖的部分都非常少。所以未来的 USB 模糊测试技术必须解决这个问题。

以下是现有的 5 种解决方案:HCD 驱动程序模糊测试、协议引导或状态模糊测试(Protocol-guided/Stateful fuzzing)、Android USB 模糊测试、协议引导或状态模糊测试、Type-C/USBIP/WUSB 模糊测试,下面我们就来详细了解一下。

HCD 驱动程序模糊测试

如果我们总是将自己限制在 USB 主机协议栈中,那么 HCD 驱动程序就会被忽略。与设备驱动程序不同,HCD 驱动程序无法通过系统调用在用户空间访问(不过可以使用 sysfs 调整某些参数)。相反,它们从上层(内部)的 USB 内核(例如,usb_submit_urb)和 HCD 层(外部)的 DMA 接收输入内容。从安全角度来看,外部输入会比内部输入带来更多的威胁。

要直接对 HCD 驱动程序的内部输入进行模糊测试,我们需要能够修改暴露给 USB 内核的内核 API 的参数,并从 HCD 驱动程序获取代码覆盖率。为了直接模糊 HCD 驱动程序的外部输入,我们需要对 DMA 缓冲区和事件队列以及来自 HCD 驱动程序的代码覆盖率进行修改。请注意,由于 TX 和 RX 的代码路径不同,因此在这两种情况下代码覆盖率通常不同。因此,我们需要一个细粒度代码覆盖率报告来反映这一情况。对 DMA 缓冲区和事件队列进行修改实际上是在构建具有模糊功能的 HCD 仿真器,对于诸如 Intel XHCI 之类的常见 HCD 驱动程序,QEMU 已经提供了相应的 HCD 模拟(例如 qemu / hw / usb / hcd-xhci.c),并且可以尝试在其中添加模糊测试功能。对于 QEMU 不提供 HCD 模拟的其他 HCD 驱动程序,需要从头开始构建 HCD 模拟。

USB 从机协议栈模糊测试

截止目前,我们还没有对 USB 从机协议栈进行系统的模糊测试。这种做法在过去是没问题的,因为我们经常假设 USB 从机不会发生恶意攻击。然而,在嵌入式系统(例如 Android 设备)中广泛使用的 USB OTG 和 DRD 控制器,已经将包括 USB 从机在内的设备也当作了攻击目标。例如,我们现在经常会用 USB 接口对手机进行充电,而没有人希望他们的手机在充电期间被黑客攻击。在架构上,Syzkaller USB 模糊测试工具设想了一种模糊 USB 从机协议栈的方法,如下所示:

 


用户空间模糊测试工具不会让用户空间模糊测试工具与 USB 模糊内核驱动程序进行通信,而是与 USB 主机设备驱动程序进行通信。这样,模糊测试工具活动将通过 USB 主机协议栈传播到 USB 从机协议栈。因此,我们需要配置内核,以便在同一个内核映像中启用所有不同的 gadget 函数,以及代码覆盖率报告。然后,我们可以模拟 USB 从机内核和 USB 从机功能驱动程序(UDC 驱动程序除外)。

 

Linux-USB Gadget 驱动框架(简称 Gadget )实现了 USB 协议定义的设备端的软件功能。相对于 Linux USB 主机驱动而言。Gadget 框架提出了一套标准 API, 在底层, USB 设备控制器 ( USB Device Controller, UDC ) 驱动则实现这一套 API, 不同的 UDC (通常是 SOC 的一部分) 需要不同的驱动, 甚至基于同样的 UDC 的不同板子也需要进行代码修改,这一层我们可以称之为平台相关层。

请注意,Syzkaller 设想了一种对 USB 从机协议栈进行模糊测试的方法,这是由于 Syzkaller 的架构和限制而产生的自然结果。由于 Syzkaller 是一个系统调用模糊测试工具,这意味着输入突变发生在系统调用参数中。但这并不意味着我们必须在系统调用层(例如用户空间)进行模糊测试。如果再次查看上图,我们可以找到一条从模糊测试工具到模糊测试目标的路径(例如,USB 主机设备驱动程序或 USB 从机驱动程序)。那么,我们怎么才能知道所有模糊输入是否都被成功传播到目标而不是被中间层过滤?这就要看基于系统调用的模糊测试是否适合内核中的 USB 模糊测试。同样,Syzkaller USB 模糊测试工具可以适应 Syzkaller 本身的限制,而不是考虑从头开始构建 USB 模糊测试工具(例如,USB 主机或从机协议栈)。

所以,缩短模糊测试路径将解决以上存在的问题。例如,我们可以通过在 QEMU 中构建 USB UDC 模拟工具 / 模糊测试工具来摆脱整个 USB 主机协议栈,直接启用 UDC 驱动程序模糊测试。但是,这并不意味着任何 DMA 写入都可以转换为对上层 USB 从机驱动程序的有效 USB 请求。因此,缩短模糊测试路径只是暂时的解决办法。所以最终的解决方案还是通过修改算法和代码覆盖粒度。最后,我们可能需要在协议栈中的不同层上使用不同的模糊测试工具,以确保所有模糊输入都被传播到目标中而不进行过滤。例如,我们可能需要构建一个 USB 主机模拟工具 / 模糊测试工具,直接向不同的 USB 从机驱动程序发送 USB 请求。

Android USB 模糊测试

通过维护自己的内核分支并实现额外的 USB 从机功能驱动程序(例如 MTP),Android 可能是 USB 从机协议栈的主要使用用户。与典型的 USB 主机相比,Android 设备中的 OTG/DRD 支持也使攻击面增加了一倍。不过最大的挑战是运行一个 Android 内核映像,其中包含使用 QEMU 的真实 Android 设备使用的相应 UDC/DRD 驱动程序。由于 SoC 可以自定义,在 QEMU 中运行非 AOSP 内核会带来额外的困难。这就是为什么许多 Android 模糊测试仍需要借助物理设备来进行。

协议引导或状态模糊测试

在 USB 从机协议栈模糊测试中,我们讨论了为什么我们可能想缩短模糊测试路径,因为我们想要避免模糊输入内容在到达目标之前被过滤。事实证明,要想实现路径的缩短,过程要比想象的复杂得多。如果我们再次查看上图(Syzkaller 中 USB 从机模糊测试),则模糊测试工具输入的进程是从系统调用开始,并在最终传送到 USB 从机协议栈之前传递不同的 USB 主机设备驱动程序。虽然模糊路径很长,并且可以在此过程中过滤模糊输入。但同时,中间的这些额外层保证发出的任何模糊输入都是合法的 USB 请求,其携带由正确的驱动程序状态触发的相应协议的有效载荷。例如,模糊测试工具经由 USB 大容量存储驱动程序生成的最终 USB 请求可能包含一个合法的 SCSI 命令 ( 例如 read ) ,该命令由 USB 主机设备驱动程序的内核逻辑而不是初始化部分触发。

这就是我称之为 " 协议引导或状态 " 的模糊测试。可以看出,在一个层中垂直 " 深入 " 是很重要的,例如,在初始化或探测阶段之后探索内核驱动程序的其他部分。简单地说,要对 USB 主机或从机驱动程序进行模糊测试,我们需要与目标建立虚拟连接(例如,确保内核驱动程序已初始化并准备好处理输入内容),并引导模糊测试工具学习输入内容(例如 USB 大容量存储中的 SCSI 协议)。最后,在包含其他层以重用现有协议和状态控件之间进行权衡,从而增加模糊路径和复杂性,并在模糊测试工具中直接实现轻量级协议引导或状态模糊测试,以减少模糊测试路径。

Type-C/USBIP/WUSB 模糊测试

除了 USB 主机和 USB 从机之外,USB 中还有更多东西,包括 USB Type-C,USBIP,WUSB 等。虽然我们可以重用 USB 模糊测试中学到的一些经验教训,但这些技术引入了不同的软件协议栈。 需要不同的注意力来解决他们的怪癖。