What You Corrupt Is Not What You Crash-Challenges in Fuzzing Embedded Devices

摘要

随着网络化嵌入式系统越来越普遍,其安全性对我们的日常生活越来越重要。虽然对这些系统的手动或自动大规模分析定期发现新的漏洞,但这些系统的分析方法通常与桌面系统上使用的方法相同。更具体地说,传统的测试方法依赖于程序的可观察崩溃,而二进制检测技术被用来改进对这些错误状态的检测。

在本文中,我们证明内存损坏是一类常见的安全漏洞,在嵌入式设备上通常会导致与在桌面系统上不同的行为。特别是,在嵌入式设备上,内存损坏的影响通常不太明显。这大大降低了传统动态测试技术的效率,尤其是模糊测试。

此外,我们还分析了不同种类的嵌入式设备之间的差异,并展示了这些差异对固件分析的影响。我们进一步描述和评估相对简单的启发式方法,这些方法可以在运行时(在执行跟踪或模拟器中)应用于嵌入式设备的分析,以检测以前未检测到的内存损坏。

1.简介

在程序漏洞分析中,fuzz通常是一种有效的挖掘途径,因此,在嵌入式设备的漏洞研究中,如果能使用fuzz的方式,应该能带来较好的效果。但是,与fuzz普通软件程序不同的是,嵌入式设备固件通常缺少完善的内存检测机制,即使内存发生损坏,也不会立即被检测到,通常会产生滞后的crash或者其他的负面影响,这就会给fuzz带来较大的困难。因此本文具体分析了嵌入式设备fuzz中的难题,并提出了一些启发式的思路,希望能给后来相关的研究做一些铺垫。

  • 分析了模糊化嵌入式设备的挑战,
  • 根据检测软件内存损坏的困难程度对嵌入式系统进行分类,
  • 评估不同内存损坏对不同类别的嵌入式系统的实际影响,
  • 描述了可用于改进嵌入式设备上模糊的技术,
  • 当嵌入式系统的固件可以在部分或完全仿真环境中运行时,我们提出了六种启发式方法,可用于检测由于内存损坏而导致的故障,
  • 在Avatar和PANDA框架的组合上实现了我们的启发式算法,并进行了一些测试,以在模糊实验中显示它们的有效性和开销。

2.fuzz嵌入式系统

A 嵌入式设备分类

嵌入式设备通过下面两个特征与现代通用计算机区分

  • 嵌入式系统是为实现一个特殊目的而设计的
  • 嵌入式系统通常通过连接到传感器和执行器的许多外围设备1与物理世界交互

根据嵌入式设备使用的OS分类

  • Type-I: General purpose OS-based devices. LINUX,windows
  • Type-II: Embedded OS-based devices. 嵌入式设备定制的
  • Type-III: Devices without an OS-Abstraction. 无os

B 过去的实验

主要是黑盒,基于变异的/基于生成的,固件仿真

C fuzz嵌入式设备的主要挑战

  • 故障检测
  • 性能和可扩展性
  • 插桩,覆盖率、内存

3.嵌入式设备中的内存损坏

  • Observable Crash :可观测到,被测设备的执行停止
  • Reboot :立即重启
  • Hang :目标挂起并停止响应新请求,可能陷入无限循环
  • Late Crash :目标系统在一定时间内继续执行,然后崩溃(例如,当连接终止时)
  • Malfunctioning :过程继续,但报告错误数据和错误结果
  • No Effect :尽管内存已损坏,但目标仍正常继续,无明显副作用

对三种设备研究了触发内存损坏时的反应。为了准确的确定内存损坏的时机,作者修改了设备固件,人为地加入了一些漏洞,在fuzz中可以使用特定的输入触发内存损坏,以便于结果的观测和统计。实验结果如图3

实验结果印证了他们的猜测,缺少完善的内存检测机制会使设备产生难以直接观测的负面影响。

另外,嵌入式设备fuzz对于硬件的依赖较大,厂商开源代码很少,工具链缺失等情况都给Fuzz的过程带来较多的困难。模拟化门槛高使得fuzz过程难以并行,效率较低。

4.检测思路

A) Static Instrumentation.

先决条件:源代码和编译器工具链

由于大多数用于嵌入式设备的软件都是以二进制方式分发的,因此对源代码的访问可视为一种例外情况。在很少有源代码可用的情况下,通常需要专有的工具链来重新编译固件。

不幸的是,大多数这些工具还不能用于嵌入式系统。

B) Binary Rewriting.

先决条件:固件二进制映像、设备

当只有二进制固件可用时,可以使用二进制重写来检测代码。但是,在检测代码时有一些困难。首先,固件需要完全反汇编,这在作为原始二进制文件提供时可能很困难。其次,内存语义、边界和数据结构在编译过程中丢失,需要恢复以添加检测。这需要一个非常具有挑战性的部分反编译阶段。最后,嵌入式设备的内存使用通常经过优化以降低成本,为添加复杂的仪器设备留下了很小的空间。

C) Physical Re-Hosting.

先决条件:源、编译器链、不同设备

在某些情况下,分析员可以为不同的目标设备重新编译代码,例如,将进程从类型II设备重新定位到更易于测试的类型I设备,或从类型I设备重新定位到常规Linux桌面系统。如果新设备比原设备更便宜、更容易获得,或者如果新目标是普通计算机,则这也可以提高可伸缩性。

然而,除了困难的先决条件外,依赖这种方法的方法还有另一个主要缺点。事实上,可能很难在原始设备中重现在新目标系统上发现的错误(由于不同的体系结构或通过重新编译二进制文件而引入的更改,这些错误可能根本不存在),相反,原始目标上存在的错误可能不存在于新目标上。

D) Full Emulation.

先决条件:固件映像、外围设备仿真

设备固件的映像通常可供分析人员使用,这可能是因为它们是直接从设备中提取的,也可能是因为它们是从制造商提供的固件更新包中获取的。

这种解决方案可以大大改善模糊性。首先,测试实验可以在没有物理设备的情况下进行,因此可以实现更大的平均化。其次,动态检测技术可以很容易地应用,仿真器可以用来收集关于正在运行的固件的大量信息。此解决方案的缺点是,它只适用于目标访问的所有外围设备都是已知的并且可以成功模拟的情况,不幸的是,这种情况很少发生。总的来说,能够在模拟器中运行任意固件仍然是一个开放的研究问题。

E) Partial Emulation.

先决条件:固件映像、设备

如果在大多数情况下完全仿真仍然不可行,那么在进行测试实验时,部分仿真仍然可以提供好处。此解决方案背后的总体思想是使用经过修改的模拟器(其中执行固件代码)将外围交互转发到实际的物理设备。结果提供了完整仿真解决方案的优点,而无需了解和仿真I/O操作。然而,这种解决方案在灵活性上得到的牺牲在性能上(由于与实际设备的额外交互)和可扩展性(由于当前需要将每个仿真实例与物理设备配对)。

F) Hardware-Supported Instrumentation.

先决条件:设备、高级调试功能(例如,支持跟踪的调试端口和调试器)

如果测试仪可以访问具有高级硬件检测机制(如实时跟踪)的物理设备,则可以在设备执行期间收集足够的信息以改进故障检测。但实际中较难,III类设备很难找到,使用中也可能受阻。

总而言之,前三个解决方案(A、B和C)要求测试人员修改固件映像,可以通过重新编译进行静态修改,也可以通过运行时检测进行动态修改。不幸的是,如上所述,在进行第三方安全测试时,这很少是一个选项。后三种技术(D、E和F)具有不需要任何固件修改的优点,但它们需要其他技术(软件仿真器或硬件跟踪支持)来收集有关正在运行的固件的信息。

5.故障检测启发式算法

  • Segment Tracking

段跟踪可能是最简单的技术,旨在检测非法的内存访问。其核心思想是观察所有的内存读写,并验证它们是否发生在有效的位置,从而以某种方式模仿MMU来检测分段错误。这种技术只需要了解目标的内存访问和内存映射。在模拟器中两者都很容易访问,但是,当只有trace可用时,可以通过逆向工程获得内存映射。

  • Format Specifier Tracking

在最简单的情况下,如果不存在动态生成的格式字符串说明符,则这些有效位置必须位于只读段中。总之,这种技术不仅需要知道格式处理函数的位置,而且还需要知道在输入这些函数之一和参数顺序时的寄存器状态。通过对固件的逆向工程或自动静态分析,可以获得相应函数的位置及其参数顺序。

  • Heap Object Tracking:

此技术设计用于检测与时间和空间堆相关的错误,并受检测和运行时验证方法的影响。它通过计算分配和释放函数的参数和返回值以及记录堆对象的位置和大小来实现其目标。这允许轻松检测出超出边界的内存访问或对已释放对象的访问。然而,这种启发式方法依赖于各种信息:执行的指令、寄存器的状态、内存访问以及关于分配和释放函数的知识。

  • Call Stack Tracking:

调用堆栈跟踪复制了传统的影子堆栈保护,因此旨在检测不返回被调用方的函数。这有助于识别覆盖函数返回地址的基于堆栈的内存损坏。它通过监视所有直接和间接的函数调用和返回指令来实现。然而,由于嵌入式设备通常是中断驱动的,这种启发式方法可能导致错误的否定。然而,它特别吸引人,因为它只需要最少的信息:只需要知道执行的指令。

  • Call Frame Tracking:

调用帧跟踪是调用堆栈跟踪技术的一个更高级的版本,它可以在出现粗粒度堆栈缓冲区溢出时检测到这些溢出,而不会出现假否定。实质上,堆栈帧是通过跟踪函数调用来定位的,然后检查相邻的内存访问以避免跨堆栈帧。因此,这需要标识执行的指令以及寄存器值,以便在函数项上提取堆栈指针值。然后,必须观察内存访问以检测实际的损坏。

  • Stack Object Tracking:

堆栈对象跟踪是受Serebryany等人提出的堆对象跟踪方法的启发,对堆栈变量的越界访问进行细粒度检测。因此,在执行期间观察到的内存读写将对照堆栈中的单个变量大小和位置进行检查。显然,这需要跟踪执行的指令和内存访问,以及有关堆栈变量的详细信息。为了简单起见,我们使用调试符号中的变量信息。然而,在一般情况下,可以从二进制代码中自动检索这些信息。

6.设计与实现

为了更好的检测内存状态,作者们提出了一些检测的方法。针对段,堆和栈开发相应的检测工具或者插件,判断内存读写的合法性;针对格式化字符串,判断字符串的只读性等思路,来更好的检测corruption。作者们也设计了实验验证这些思路的可行性,结果显示,使用综合检测的方法可以检测出所有的corruption。

7.总结

本文针对现存的嵌入式fuzz困难做了归纳分析,也提出了相应的解决思路,并通过一些简单的实验验证了思路的可行性。但整体上都是一些针对一些用于测试的案例进行验证,并不能直接应用到真实的场景中,另外,提出的几点解决思路在实现上都存在较多的困难,距离可用还有很长的一段路要走。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值