Firm-AFL:高效的IoT固件灰盒fuzz
2021-03-05 18:58:00 Author: mp.weixin.qq.com(查看原文) 阅读量:129 收藏

本文为看雪论坛精华文章

看雪论坛作者ID:pureGavin

摘要

对 IoT 设备的网络攻击是一个严重威胁。这些攻击利用 IoT 固件中的软件漏洞。fuzz是一种有效的软件测试技术,用于漏洞挖掘。在IOT漏洞挖掘工作中,我们介绍Firm-AFL,用于 IoT 固件的第一个高通量灰盒模糊器。Firm-AFL解决了物联网模糊中的两个基本问题:
1、它通过启用模糊处理来解决兼容性问题。可在系统中模拟的 POSIX 兼容固件仿真。
2、它解决了性能瓶颈系统模式仿真技术采用新型技术引起称为增强过程仿真。通过组合系统模式仿真和用户模式仿真以新颖的方式,增强过程仿真提供高兼容性,如系统模式仿真和高吞吐量作为用户模式仿真。
我们的评估结果显示:
(1)Firm-AFL功能齐全,能够挖掘到现实世界中的IOT设备漏洞;
(2)FIRM-AFL 的平均吞吐量比基于系统模式仿真的fuzz高8.2倍;
(3)Firm-AFL 能够找到1day漏洞比基于系统模式仿真的fuzz速度快得多,并且能够找到0day漏洞。
 

一、简介

物联网设备对我们的隐私安全的影响非常大。据不完全统计直至2017年为止,全球物联网设备超过了82亿,人们的生活都离不开IOT设备,IOT设备的不安全问题已迫在眉睫。目前黑客对IOT设备的主要恶意行为是创建大型的僵尸网络(例如:Mirai、VPNFilter和Prowli)。恶意程序会通过IOT设备的1day或0day直接拿到root权限。因此,防御者必须要在攻击发生之前找到漏洞并修补。
Fuzz是一种软件测试技术,通过给目标程序发送变异的样本可以高效的进行漏洞挖掘;其中AFL(American Fuzzy Lop,一种以代码覆盖率作为变异策略的fuzz)的知名度最高,在DARPA Cyber Grand Challenge的决赛中,大部分选手都会使用AFL做漏洞挖掘。
IoT设备fuzz所存在的问题:
尽管fuzz在通用平台上能对程序进行有效的测试,但是因为fuzz对硬件配置有较高的要求,所以一般情况下不能直接在IOT设备上进行fuzz,比如:提取一个固件并找到一个应用程序,然后使用AFL对此程序进行fuzz,正常情况下fuzz会失败。
为此,最近的研究者提出了一系列的解决方案,从直接对IOT设备进行fuzz(例如:IOTFuzzer_Full);一种结合结合了硬件和软件的混合fuzz(例如:AVATAR);到完全使用软件进行系统模拟(例如:Firmadyne)。最近由Muench et al的研究报告指出:使用软件进行系统模拟的效率要比使用IOT设备的效率高得多,因为大多数IOT设备的硬件配置本身并不高。
吞吐量是影响fuzz效率的关键因素;然而即使是使用软件进行系统仿真,其性能也并不理想,根据我们的测试结果来看,system-mode大约比user-mode慢十倍(在使用AFL的情况下);慢十倍也就意味着同样是发现一个漏洞,system-mode要多花十倍的系统资源。根据我们的分析来看,system-mode主要开销有两个,一个是将虚拟内存转换为物理机内存,即内存管理单元(例如:SoftMMU);另一个主要开销是模拟系统调用的开销。
我们的解决方案:通过增强过程仿真进行灰盒模糊处理
在研究中我们发现,这是据我们所知第一个能同时完成以下两项设计目标的IOT固件fuzzer:1、透明性:在进行fuzz之前不需要对固件的代码进行修改;2、效率:整个system-mode的吞吐量接近于user-mode的吞吐量。我们找到了能实现这两个目标的关键技术,也就是同时兼容了system-mode的通用性和user-mode的效率。
更准确的说,我们提出了一种名为“增强过程模拟”的新技术。顾名思义,这项技术的主要思想就是通过完整的系统模拟来增强进程模拟(或user-mode)。被fuzz的程序大部分时间会在user-mode中运行以提升效率,只有某些特殊时刻程序才会在system-mode中运行,以保证程序能正确执行。
为了评估这项技术的可行性,我们结合了AFL和Firmadyne,实现了一个名为FirmAFL的fuzz原型。从用户的角度来看,当用户使用Firm-AFL时,一方面可以像普通AFL做fuzz那样对固件进行基于代码覆盖率的fuzz,另一方面Firm-AFL偶尔也会切换到Firmadyne进入system-mode,以确保程序能够正常执行。
我们已经用一组真实的IOT固件来对Firm-AFL进行基本评估,结果表明:
(1) 就想使用system-mode运行固件一样,Firm-AFL可以正确的运行固件;
(2) Firm-AFL的平均吞吐量相比于TriforceAFL(此fuzzer启用了轻量级的快照)这样的基于完整system-mode的fuzzer快了8.2倍;
(3) 在单个机器上Firm-AFL找到1day漏洞的速度比使用system-mode做fuzz的fuzzer快了3到13倍,并且在8小时内找到了两个0day漏洞。
 

贡献

综上所述,我们在本论文中作出了如下贡献:
1、我们认为,全系统仿真的运行开销很大,当批量进行固件fuzz时,效率并不理想;我们将进一步研究开销大的根本原因;
2、摘要针对全系统仿真(高通用性、低效率)和用户模式仿真(低通用性、高效率)的矛盾特点,我们提出了一种“增强过程仿真”的新技术;
3、我们为IOT固件设计并实现了第一个基于代码覆盖率的灰盒fuzzer,Firm-AFL;
4、我们对我们的系统进行了全面的评估,并打印出系统的各个部分的运行开销;我们的改进使得fuzz的平均速度提高8.2倍,并且能够在8小时内挖掘到两个0day
5、  目前最新版本的Firm-AFL支持三种CPU架构:mipsel、mipseb和armel,这涵盖了Firmadyne数据库中90.2%的固件。Firm-AFL源码已经上传至GitHub:https://github.com/zyw-200/FirmAFL

二、背景和原因

2.1 Fuzzing

Fuzzing是一种软件测试技术,旨在通过执行带有随机输入的目标程序并寻找有趣的程序行为(例如:crash)来发现bug。根据执行时收集和使用的信息的多少,fuzzer可以被分为黑盒、白盒和灰盒。黑盒fuzzer将程序视为黑盒,不利用任何执行反馈来做随机输入的变异,这种方法最初被用于Linux的程序测试;相反的,白盒fuzzer是根据对目标程序有着深入的了解下,对随机输入进行变异,这类程序分析技术通常代价较大,例如动态污点分析和符号执行;最后则是灰盒fuzzer,此类测试会根据有限的信息(例如代码覆盖率)来对随机输入进行变异。
目前最流行的灰盒fuzzer变异策略是根据代码覆盖率进行变异。这些fuzzer检测目标程序以收集代码覆盖率信息,之后收集到的信息会被用来指导样本的变异——能触发新代码的样本会被遗传算法选中,而不能触发新代码的样本会被丢弃。这种简单的策略在实际应用中效率很高。事实上,在实际应用中,因为灰盒fuzzer的速度很快甚至可以胜过白盒fuzzer,灰盒fuzzer的轻量级使得灰盒fuzzer的执行速度比白盒fuzzer快百倍甚至千倍,换言之,吞吐量对灰盒fuzzer至关重要。
AFL是著名的灰盒fuzzer。它可以静态或动态的分析程序。当有源代码时,AFL就会静态插桩;当没有源代码时,例如:fuzz商业程序(COTS)时,AFL会使用二进制转换器(例如:qemu的user-mode)来执行检测。因为大多数物联网设备的源代码和设计文档是专有的,所以只能进行动态检测。事实上就算是固件提取也要费一番功夫。

2.2 QEMU

QEMU是一个基于动态二进制转换快速处理器的模拟器。与传统的逐条执行目标程序的模拟器不同,qemu一次性翻译多个基本块,并使用块链将它们连接在一起。这种块链设计使得qemu大部分时间都会在代码缓存(cache)内执行,从而使得转换开销最小化。动态检测可以在转换过程中执行以引入新功能,例如:分支监视和污点传播。
除了指令翻译外,还有一个重要的任务是地址空间翻译。在不同的执行模式下翻译的方式会有很大的不同。在系统模式下,qemu实现了一个软件内存管理单元(MMU)来处理内存访问。软件MMU将虚拟机的虚拟基址(GVAs)映射到物理机的虚拟地址(HVA)。这个映射过程对客户操作系统(OS)是透明的,这意味着qemu允许客户操作系统通过页表的接口设置GVA到客户物理地址(GPA)通过页面表的界面映射处理页面错误。在底层,qemu为每个内存访问插入一个从GVA到GPA的转换逻辑。为了加快翻译速度,qemu使用转译后备缓冲区(TLB)来缓存转译的结果。此外,为了避免在地址转换发生时使代码缓存和块链失效,所有已转换的块都是用GPA进行索引,并且只有在两个基本块位于同一物理页面内时才执行块链。GPA到HVA的映射使用线性映射(即 HVA=GPA + offset)。
与system-mode不同,在user-mode中主机虚拟地址(HVA)的计算方式为:客户虚拟地址(GVA)+ 常量偏移量。因此,这种转换比system-mode中的转换快得多。

2.3 测试IOT固件

随着IOT设备成为热门的攻击目标,IOT固件的漏洞挖掘也变得越来越重要。在测试程序时有两个挑战,第一个挑战是兼容性:许多IOT程序依赖于设备的特殊硬件组件,因此如果没有硬件支持将无法继续测试;第二个挑战是代码覆盖率:众所周知,黑盒fuzzer的代码覆盖率很低,而白盒fuzzer在代码量很大的情况下容易跑崩。表1对各种fuzzer的性能进行了对比。
AVATAR旨在通过提供更好的硬件组件支持,进行嵌入式固件的动态分析。它通过构架一个有处理器模拟器(qemu)和真实硬件组成的混合执行环境来进行固件分析,其中AVATAR充当的是qemu和真实硬件之间的软件代理。这使得AVATAR可以利用模拟器来执行和分析指令并将I/O操作传输到物理硬件上。作者使用S2E(白盒fuzzer)来挖掘Redwire Econotag Zigbee传感器的漏洞。因为是白盒fuzzer并且物理硬件的配置也不高,预计AVATAR的吞吐量会极低。
IOTFuzzer直接在真实设备上执行黑盒fuzz。与传统的基于黑盒的fuzz相比,他的主要优势就是通过目标设备的配套固件来执行fuzz。通过自动分析配套固件程序中的数据流,可以更好的理解通讯协议,从而生成更好的测试样本,提高触发bug的几率。也就是说,根据我们的评估,IOTFuzzer从来没有超过‘1样本/秒’的吞吐量,这是很慢的。
Firmadyne并不执行fuzz,它只是增加了qemu-system-mode对固件支持的数量。它提供了对ARM和MIPS架构的支持,这些架构在商业中很流行。在硬件模拟方面,Firmadyne通过修改内核和驱动程序来完全模拟系统,以处理由于硬件的缺乏而导致的大量异常。与前两种解决方案相比,该方案能很快的适应新的IOT固件。全系统模拟的吞吐量通常比IOT设备要好。
Muench等人比较了不同配置下的黑盒fuzzer的吞吐量,包括:IOT设备执行(直接将输入发送到物理设备)、部分模拟(只发送硬件请求)和完全模拟。他们的仿真基于PANDA提供的图像重播功能。他们得出的结论是完全模拟(FE)具有最高的吞吐量,主要是因为IOT处理器要比桌面级处理器慢很多。然而即使是桌面级处理器,吞吐量也不会超过‘15样本/秒’。
AFL是一个非常著名的灰盒fuzzer,它可以通过qemu-user-mode进行二进制fuzz。只可惜,因为缺乏必要的硬件支持,qemu-user-mode并不能模拟IOT固件程序。例如:使用支持qemu-user-mode的AFL对固件进行模拟时全都失败了(表三)。此外,因为吞吐量第的问题,直接采用Firmadyne进行完整的系统模拟并不可行。
总而言之,现有的固件fuzzer并不能做到代码覆盖率的最大化,而最先进的fuzzer(例如:AFL)却因为硬件的原因,无法直接用于固件fuzz。目前为止还没有高效的IOT灰盒fuzzer。
表1 固件测试工具的比较

2.4 动机

鉴于目前IOT固件fuzzer的不尽人意的现状,我们的目标是为IOT固件设计一个高吞吐量的灰盒fuzzer。为此,我们决定在模拟的基础上设计一个fuzzer。这么做是出于两点考虑:首先,灰盒fuzzer需要收集代码覆盖信息来作为样本变异的依据,正如$2.1中所说,代码覆盖率检测并不是很难,由于大多数IOT固件只有二进制程序,因此工具最好是基于模拟器的;其次是性能,根据Muench等人的研究表明,虽然可以直接在IOT设备上执行fuzz,但是因为硬件配置的原因,使用完全模拟会更快,因为桌面级CPU的处理速度更快。
Firmadyne最大的问题是吞吐量不够,因此即是在system-mode下进行fuzz,速度也不会超过‘15样本/秒’。为了找到限制吞吐量的原因,我们使用了两种工具(basename和uptime)分别测试在system-mode和user-mode下的执行时间,结果如表2所示。根据测试结果可得出:如果在fuzz时使用user-mode将明显的提高吞吐量。导致时间差异的原因如下:
B1、内存地址转换。在system-mode中,qemu使用软件模拟MMU对所有内存访问进行地址转换;相比之下,user-mode的地址转换要简单的多。因此,即使只考虑user-mode本身执行所花费的时间,也要比system-mode少得多。
B2、动态代码翻译。User-mode下做代码翻译比system-mode要快;在system-mode下,块链接仅限于同一物理页中的基本块,这意味着在user-mode中调用转换程序的频率更高。
B3、系统调用模拟。在user-mode中系统调用是由主机操作系统和硬件直接处理的,因此它比system-mode要快得多,因为在system-mode中操作系统和硬件设备都得被模拟;因为需要让程序正确的执行,就需要硬件模拟,但并不是所有的系统调用都需要依赖硬件模拟,换言之,并不是所有的系统调用都需要模拟。
在本次工作中,我们解决了上述的三个问题,并大幅提高了fuzz的吞吐量。
表2 system-mode和user-mode的运行时性能

三、增强进程模拟

3.1 概述

这项工作的目标是设计一个IOT固件的高吞吐专有灰盒fuzzer,在$2中提到,想要实现这个目标就需要解决两个问题:兼容性和性能;system-mode可以解决兼容性的问题,但是性能较差;user-mode可以解决性能问题,但是兼容性较差。我们提出了一种名为“增强进程模拟”的新技术,它同时拥有了system-mode和user-mode的优点。

问题陈述

一般来说增强进程模拟的目标是在user-mode中正确执行大量固件程序,并满足以下要求:
(1) 可以在系统模拟器(例如qemu-system-mode)中正确的执行IOT固件,所幸,大部分IOT固件都能在Firmadyne下运行;
(2) 固件为POSIX兼容的操作系统。所幸许多固件镜像都是用Linux作为操作系统。
通过增强进程模拟,我们将实现如下目标:
1、透明度。在增强进程模拟中运行的用户级程序的行为应该与在system-mode用运行的程序一样。
2、效率。由于吞吐量是样本变异的主要因素,增强进程模拟需要尽可能高效。理想情况下,它应该接近于纯user-mode的性能。

解决方案概述

为了实现上述设计目标,我们采用了一种新颖的方式对system-mode和user-mode进行混合。图1简单解释了我们的方法。
图1 增强进程模拟的概述
首先,IoT 固件在系统模式下启动,模拟器和用户级程序(包括fuzzer)在模拟器内正确启动。当被fuzz的程序达到预定的点(如main函数的入口点,或接收到第一个网络数据包)后,进程的执行被迁移到user-mode,以获得较高的执行速度。只有在极少数情况下,程序会回到system-mode执行,以保证程序能正确执行。
为了降低迁移成本,将会在这两种模式之前共享内存。更具体地说,system-mode的物理内存被分配为一个内存映射文件(称为RAM文件)。这个RAM文件也映射到user-mode的地址空间。但不同的是system-modeuser-mode会以不同的方式访问这个RAM文件;system-modeRAM文件视为物理内存,因此通过物理地址访问它;而user-mode通过虚拟地址访问共享内存。
因此,RAM文件中的物理页面需要按照页面粒度通过它的虚拟地址映射到user-mode的地址空间。因此,当user-mode未建立页面映射时,需要将进程迁移到system-mode以建立此映射。我们将在$3.2中详细介绍内存映射的细节。
在到达系统调用之前,只要有适当的内存映射,进程就能在user-mode中正确执行。而在到达系统调用时,如果直接在物理机系统中执行固件的系统调用一般不会成功,因为物理机和固件不仅是软件不同,硬件也不同。为了确保透明性,我们需要将执行迁移到system-mode中来处理系统调用,当系统调用结束时,我们将执行迁移回user-mode;我们会在$3.3中详细讲解技术细节。

3.2 内存映射

引导

用AFL对程序做fuzz时,当程序执行到预定点,AFL的fork服务器会在这个点上重复fork一个新的程序实例(这个点称为fork point),并提供随机样本输入。同样的,在固件fuzz中,我们将在system-mode中启动固件,并启动指定程序。使用DECAF(基于system-mode的动态分析平台)提供的虚拟机自省(VMI),我们能够监控指定程序的执行,并在执行到预定点时得到通知。
此时,我们将遍历指定进程的页表,收集虚拟到物理页映射信息,并将其发送到user-mode。然后对每次虚拟地址到物理地址的映射都通过user-mode建立映射,建立映射所需要用到的mmap函数如下:
mmap(va, 4096, prot, MAP_FILE, ram_fd, pa);

上述代码顾名思义。本质上,我们将RAM文件的一个物理地址作为偏移量映射到指定的虚拟地址。参数prot由对应的页表项的保护位决定。
从此开始,system-mode暂停运行,CPU状态被发送到user-mode,然后在user-mode继续运行。

页面错误处理

在user-mode的进程执行期间,如果访问内存地址已经映射在此地址空间,那么程序执行不会出错。否则,物理机CPU将引发页面错误。我们在user-mode中为页面错误注册了一个信号处理程序,如果出现页面错误时物理机系统会把页面错误事件传递给user-mode,当收到这个信号时,user-mode会记录故障处指令的CPU状态,然后暂停执行,并将CPU状态传递给system-mode,期望system-mode能处理页面故障并建立故障虚拟地址的新映射。
当system-mode接收到CPU状态并恢复执行时,模拟的CPU会因为页面不存在而引发一个页面错误。页面错误处理程序将在物联网固件的操作系统中响应此页面故障,并尝试建立映射。大多数情况下,操作系统会建立一个类似映射(取决于内核线程和中断处理程序的调度),而导致页面错误的指令也将被重新执行;极少数情况下,如果操作系统因为某些原因无法建立映射,那么进程将被杀死。
这里的一个关键问题是确定何时建立了页面映射或何时发生错误,这样我们就可以在不同模式之间快速切换,以最大限度的提高执行速度。事实上这个问题的答案是很重要的,因为操作系统会同时处理多个任务,可能会同时发生大量的页面切换。
我们会用工具测量每个基本块的结束时间,以确认映射建立的时间。如果当前执行在指定的进程(或线程)内,就说明程序已从内核返回到用户空间以恢复错误指令。该映射必须在TLB中存在,以便我们可以直接找到这个映射。此时,我们将映射信息和CPU状态传递回user-mode,而user-mode将通过调用mmap函数创建新的映射并继续执行。
如果因为某些原因出现了错误,进程被杀死了,我们可以通过DECAF提供的虚拟机自省(VMI)功能收到消息,然后终止两边的执行。

预加载页面映射

当前的操作系统会在进程启动时以一种惰性的方式加载内存,所有的代码页都被分配到它的地址空间,直到第一次内存访问导致页面错误之前,都不会建立虚拟页到物理页的映射。
这种惰性设计对fuzzer的性能有不利的影响。正如我们在$4.1中讨论的,每个fuzz迭代的子进程都重复的从父进程派生出来。因此,总有一堆由未映射代码页引起的页面错误。这对我们的系统伤害极大,因为处理页面错误的开销远比在物理机系统上做本地处理大得多。
为了解决此问题,我们决定在物理内存中预加载给定的进程代码页,并在两种模式之间执行映射。这有助于我们避免在每次fuzz迭代时重复加载代码页,从而提高fuzz吞吐量。为此,我们在引导过程中模拟system-mode对每个程序代码页的访问,以强制操作系统将每个页映射到进程的地址空间,从而减少有这些预加载页面引起的页面错误的数量。

3.3 系统调用重定向

程序中的系统调用会因为底层硬件、固件和需求的不同而不同。因此,如果没有正确的处理由系统调用引起的异常,user-mode下的IOT固件可能会崩溃;例如:大多数IOT设备都有网络接口,而本地模拟器上没有此接口;当user-mode中的IOT程序执行需要与IOT系统中特定的网络接口交互时,就会引发故障;另一种情况时访问桌面计算机未定义的NVRAM的系统调用。
因此,为了确保固件能正确执行,我们必须将系统调用从user-mode重定向到system-mode。更具体的说,当user-mode遇到系统调用时,他会暂停执行,保存当前的CPU状态,并将其发送给system-mode;system-mode接受到CPU状态并继续执行,这将导致客户系统直接切换到内核模式以处理相应的系统调用。同样,由于客户系统的内核是多任务的,在系统调用返回之前可能会发生环境变化;因此,我们会用处理页面错误的方式来处理此问题,在每个基本块的末尾安装工具。如果当前的基本块在内核空间(r0),但下一个程序计数器在用户空间(r3),并且当前执行环境是针对执行系统调用的线程的,那么我们就会检测系统调用何时返回;此时system-mode暂停执行,保存CPU状态,并将其传递回user-mode,然后继续执行。

优化与文件系统相关的调用

在检查IOT程序发出的系统调用时,我们发现许多系统调用都与文件系统有关。IOT固件程序要么访问已有的文件或目录,要么就临时创建文件或目录。我们决定对这组系统调用做一些优化。我们从固件中映射文件系统,并将其作为物理机操作系统中的一个目录挂载,这样user-mode就可以直接访问它了;同样地,user-mode就可以直接将文件系统相关的系统调用传递到物理机操作系统,而不是重定向到system-mode。
正如$5.3所示,与文件系统相关的系统调用会大幅消耗系统资源,因此这种优化对整体性能影响很大。

四、Firm-AFL设计与实现

利用$3中介绍的技术,我们设计了基于AFL的IOT固件fuzzer——Firm-AFL。在$4.1中,我们将会介绍AFL的工作流程,然后在$4.2中,我们会介绍如何将增强进程仿真集成到AFL中。

4.1 AFL工作流程

AFL是一个根据代码覆盖率进行变异的灰盒fuzzer。它维护了一个样本集,该集合储存了所有的样本,包括用户选择的初始样本,以及从现有样本中变异而来的新样本,这些样本会使程序的代码覆盖率最大化。
启动AFL的主程序在afl-fuzz中。它从样本集中挑选一个原始样本执行一次随机变异,生成一个新的随机样本,并将这个样本喂给目标程序(假设目标程序是一个二进制可执行程序)。
AFL会使用qemu-user-mode启动程序,并从fuzz过程中收集目标程序的代码覆盖率信息,以检测分支转换。目标程序的代码覆盖率信息被编码并储存在位图中。
由于在fuzz过程中需要反复执行目标程序,AFL利用“fork”这种机制来提高效率。它首先运行目标程序到特定位置(例如:main函数入口),使程序的代码和数据正确初始化,然后重复的从它派生出子进程;通过这种方式可以快速跳过新进程的初始化设置。因此,父进程被成为fork-server;然后将样本喂给派生出的子进程中,覆盖率信息被收集并储存在位图中;而位图在三个进程(afl-fuzz,fork-server,子进程)之间共享。Afl-fuzz将比较子级中的位图实例和过去所有执行结果积累的位图,已确定是否应该将这个变异的样本保留并存储在样本集中。

4.2 带有增强进程模拟的AFL

我们希望在不改变原有AFL的情况下,加入对IOT固件fuzz的功能。为此,我们将qemu-user-mode替换为增强进程模拟,其余组件保持不变。新的工作流程如图2所示。
图2 Firm-AFL概述

引导

为了fuzz IOT固件中的程序,我们需要启动固件镜像,并在系统启动后启动程序。这是在fork-server中的system-mode中完成的。
我们利用Firmadyne来模拟固件。我们还在Firmadyne中加入了DECAF中的虚拟机自省(VMI)功能。这样我们就能够知道目标程序何时启动或终止,还可以知道目标程序何时执行到指定位置。

分支

AFL选择的默认分支点是main函数的入口点。在我们的案例中,我们希望通过网络接口触发IOT程序中的漏洞;因此我们会hook网络相关的系统调用,当这些系统函数被第一次调用时就会创建分支点。
在AFL的工作流程中,我们可以简单地利用系统调用的分支点来派生一个子进程并启动一个新的fuzz实例。而在我们的例子中,我们不仅需要为user-mode派生一个子进程,而且还需要为system-mode派生一个虚拟机实例,因为两个模式必须同步。
事实上,创建一个虚拟机需要的系统资源太多,所以我们会创建一个虚拟机的快照;当fuzz完成时我们可以恢复快照。qemu-system-mode提供了保存快照的功能,将所有CPU寄存器和内存空间保存到特定的文件;然后读写这些文件的速度依然缓慢。
在我们的系统中,我们基于写时复制原理实现了一个轻量级的快照机制。更具体地说,我们首先将映射到qemu-system-mode的RAM文件标记为只读;那么,内存写操作将导致页面错误,我们复制这个页面,然后把这个页面标记为可写。因此我们记录在一次fuzz期间修改的所有内存页;当恢复快照时,我们只需要将这些记录下来的页写回即可。

样本输入

输入是通过检测系统调用提供的。对于从网络接口接收输入的IOT程序,我们直接在user-mode中检测与网络相关的系统调用;因此我们不需要将这些系统调用重定向到system-mode。
收集代码覆盖率信息。由于大部分执行都发生在user-mode中,而system-mode仅用于处理页面错误和部分系统调用,所以我们可以像原始的AFL那样在qemu-user-mode中插入分支转换来计算覆盖位图。

五、评价

在本节中,我们会评估我们的Firm-AFL fuzzer。本节的目的是测试我们的方法是否已经解决了性能瓶颈并实现了两个目标;简而言之,我们会回答以下几个问题:
透明度。Firm-AFL在对固件进行fuzz时是否可以像system-mode那样正确的执行IOT程序?
效率。Firm-AFL的吞吐量(样本/秒)相比于完全基于user-mode的fuzzer的吞吐量如何?
优化的有效性。我们的优化技术成功的解决了已知的性能瓶颈吗?
漏洞挖掘的效率。在IOT固件漏洞挖掘方面,Firm-AFL到底是否有用?

实验设置

我们在测试时使用了三套程序。第一组程序时两个标准:nbench和lmbench。它们用于测试模拟的正确性和系统资源开销。第二组程序由来自四个不同的供应商的七个IOT程序组成(表3)。
我们选择这些程序是因为它们是处理网络请求的关键程序,因此是远程攻击的理想目标。它们用于测试灰盒fuzzer的性能。第三个数据集时Firmadyne数据集,其中包括了其HTTP和uPnP服务与15个1day攻击的相关固件(表6)。我们收集这些数据是为了评估Firm-AFL漏洞挖掘的透明度和效率。
实验(除了$5.4外)是在一台8核Intel(R)Core(TM)i7-3940XM 3.00GHz CPU和23.5GB RAM 1TB硬盘上进行的。操作系统为Ubuntu 16.04.5 LTS。qemu和AFL的版本分别为2.10.1和2.06b。我们在每十次迭代后获得所有测量值。我们最终报告的数字是20次测量的平均值。默认情况下,我们将分支点设置在接收到网络数据后的位置,并由AFL引擎提供随机输入。

5.1 透明度

为了评估增强进程模拟的透明度,我们首先使用nbench测试软件进行测试。在生成输出之后,基准测试将把实际输出和预期输出进行比较。如果生成的输出是错误的,则意味着模拟出错了。结果表明,该系统能够在无误差的情况下完成所有的基准测试。
我们还利用Frimadyne的数据库对Firm-AFL的透明度进行了评估。我们收集了120个带有HTTP服务和独特设备模型的固件镜像。我们从固件镜像中提取并使用chroot挂载文件系统;但是因为系统环境的原因,这些程序刚启动就崩溃了;然后我们在尝试system-mode和增强进程模拟下使用正常样本输入(初始样本)并运行;结果是在这两种模式下所有的程序都能正常运行。对于固件程序,我们需要进一步比较system-mode和增强进程模拟下生成的系统调用序列,确认系统调用的序列是相同的。
最后我们对每个表6中的漏洞我们都使用全system-mode和增强进程模拟进行复现和利用并进行了执行跟踪。我们已经确定两种模式的执行轨迹是相同的。
综上所述,本次测评表明Firm-AFL可以像全system-mode那样提供完全透明的模拟。
表3 测评中使用的IoT程序

5.2 效率

Bench跑分

我们从两个角度测试进行效率测试。首先我们使用bench跑分对增强进程模拟的性能开销进行评估。
nbench的结果如表4所示。nbench是一个测试CPU的测试组件。这个测试的增强模式并没有施加太多开销,主要是因为这些测试并没有很多内存同步操作和重定向系统调用,所以相对简单;为了测试重定向系统调用产生的开销,我们使用了lmbench。结果如表5所示,可以看到对于普通的系统调用(例如文件相关的系统调用),开销几乎可以忽略不计;但是对于需要重定向的系统调用(例如TCP相关的),开销就要大得多。

4 nbench结果,单位是“迭代/秒”;最后一列显示了增强模式的速度下降比例

表5 lmbench系统调用测试结果,单位为“微秒”
最后一列显示了增强模式的开销

Fuzz吞吐量

在第二次测试中,我们测试了Firm-AFL在不同优化下的吞吐量:
基本优化:使用TriforceAFL做基本优化。TriforceAFL使用全system-modeIOT fuzz。为了避免重新创建虚拟机,我们对Triforce-AFL中添加了QEMU的快照机制(qemu-savevmqemu-loadvm);我们还使用DECAF提供的VMI来确定程序何时启动和终止。
轻量级快照:本次配置中我们使用的是轻量级的快照($4)。
增强进程模拟:我们将模拟模式从system-mode切换到增强进程模拟($3)。
Full:我们使用了所有可以使用的优化,包括选择性重定向系统调用。
图3直观的显示了优化后的吞吐量。使用轻量级快照使得吞吐量提高了9.3倍(b和a);使用增强进程模拟使吞吐量提高了3倍(c和b);使用选择性重定向系统调用使吞吐量提高了2.9倍(d和c);相比与全system-mode,Firm-AFL使吞吐量平均提高了8.2倍。

图3 不同优化下的Firm-AFL的吞吐量;x轴是优化类型:a、基本优化;b、轻量级快照;c、增强进程模拟;d、选择性重定向系统调用

5.3 优化效果

在$2.4中,我们了解到全system-mode的三个主要的性能瓶颈:内存地址转换、动态代码转换和系统调用。在本节中,我们会评估我们的优化技术是否能解决这些性能瓶颈。为此我们将执行所花的时间分为五个部分:
用户执行时间:执行目标程序所花费的时间,包括了花在软件地址转换上的时间。
内存同步时间:在增强模拟模式下,用于设置user-mode和system-mode之间的内存映射的时间。
代码翻译时间:翻译目标程序所花费的时间。
系统调用执行时间:在执行一次迭代时花费在系统调用上的总时间。
重定向系统调用时间:在增强模拟模式下,将系统调用重定向到system-mode所花费的时间。
快照时间:在一次fuzz迭代中用于储存和恢复内存和CPU状态的总时间;值得注意的是不通的快照机制所需的时间开销并不相同,我们记录了每个页面存储和恢复操作的开始和结束时间。

轻量级快照

快照开销只存在于system-mode。在增强进程模拟中,需要一种同步机制来保证system-mode和user-mode之间快照的一致性。在本次试验中,我们测量了快照同步成本并将其添加到快照开销中。对比图4和图5我们不难发现轻量级快照使得开销减少了至少一百倍。
图4 执行时间明细:system-mode w/o 和 w/轻量级快照
图5:执行时间明细:增强进程模拟 vs system-mode

增强进程模拟

图5为了7个IOT程序的system-mode和增强进程模拟的执行时间的分解图。除dnsmasq之外的程序总执行时间平均减少了50%以上。观察执行时间的分解图可得知用户执行时间和代码翻译时间大幅减少了,用户执行时间(绿色条)平均减少了9倍;这主要是由于消除了软件地址转译,即使我们将内存同步时间合并,执行时间仍减少了大约5倍。
另一个对时间影响极大的是代码翻译时间。正如$2中提到的,这是两种优化技术。首先,在system-mode下运行时,qemu只对同一物理页面的基本块执行块链接。这意味着必须调用模拟器解决页面之间的控制传输,而在增强进程模拟中,qemu可以链接任何已经经过翻译的基本块。其次,当使用system-mode进行fuzz时,Triforce在处理每次输入后将重置虚拟机,尽管我们已经使用了轻量级快照,但是每次迭代时代码缓存都会重置,进而重复翻译相同地基本块。在增强进程模拟中可以利用AFL的代码缓存池技术来避免这种重复翻译;因此,代码翻译平均时间会变得非常小。
然而,用户执行时间和代码转换时间的减少的代价就是增加系统调用的时间,换言之,增加了系统调用执行时间和系统调用重定向时间;通常,系统调用目标程序问题越多,重定向开销就越高。dnsmasq会发出超过一千个系统调用,导致了超过两千个system-modeuser-mode之间的状态转换,从而导致dnsmasq在重定向系统调用上花费的时间比其他程序多得多;这也就是为什么我们会使用选择性重定向系统调用。

选择性重定向系统调用

图6展示了在使用和不使用选择性重定向系统调用的执行时间明细。回想一下,将系统调用重定向到system-mode是为了使程序能正确执行,然而并不是所有的系统调用都需要特殊的内核和硬件支持;因此,通过在本地执行主机系统完全支持的系统调用(例如:与文件系统相关的系统调用),可以在不使程序崩溃的同时大幅减少系统调用时间。
如图所示,优化后的系统调用执行时间大幅减少,因为现在许多系统调用都是有主机操作系统执行的,没有地址/代码转换和设备模拟;同时我们还观察到重定向系统调用时间的减少,这对会发出很多系统调用的程序有很大的影响(例如:dnsmasq)。大多数有dnsmasq发出的系统调用是文件操作,可以通过在主机操作系统中挂载IOT固件文件系统来本地处理;这样dnsmasq的总执行时间又可以减少14倍。
总而言之,根据评估结果表明我们的解决方案(增强进程模拟和选择性重定向系统调用)已经成功解决了我们在$2.4提到的三个瓶颈。
图6:执行时间明细:增强进程模拟w/O和w/选择性重定向系统调用

5.4 漏洞挖掘

本节中,我们会对Firm-AFL在真实情况下的漏洞挖掘能力进行评估。

数据收集

从Firmadyne数据集开始。我们收集了这些固件镜像并测试了仿真条件和网络可达性,然后通过探测HTTP和uPnP服务的端口来检查他们是否活跃。最后我们获得了288个激活HTTP和uPnP服务的固件镜像;然后我们使用getsploit从在线资源中收集针对HTTP和uPnP服务的漏洞,如:exploit-db、metasploit和Packet Storm;然后我们将这些漏洞输入288个镜像中,最终确定了15个可以成功利用的漏洞,这些漏洞分布在51个固件镜像中。表6列出了这15个漏洞。
我们在qemu-user-mode中运行与这15个程序漏洞相关的程序,并观察到在最后5个漏洞中只有一个与之相关的程序tcapi可以在qemu-user-mode中继续工作;这再次证明了增强进程模拟的必要性。

实验设置

本次实验的重点是fuzz HTTP和uPnP服务,这两个服务都具有良好的协议格式结构;为了使fuzz更高,我们在AFL中使用了字典“-x”;我们分别从honggfuzz、uPnP和HTTP CGI服务(直接从二进制文件中提取)中收集了HTTP的关键字。我们给每个服务都提供一个正常的服务请求作为初始样本。
除此之外为了避免低估使用默认快照实现的system-mode的性能,我们在其中启用了轻量级快照。
实验是在40核的服务器上进行的:intel Xeon(R) E5-2687w(v3) 3.10GHZ CPU, 125GB内存RAM。
最后,为了确保我们能清晰明了的观察到fuzz的性能统计结果,我们采用了Klees et al.提出的方法:在24小时内并行运行这10个fuzz实例;除了Firm-AFL之外,我们还评估了具有轻量级快照支持的system-mode。我们在AFL输出文件中使用plot_data来报告这段时间内的crash数量。
表6 1 day漏洞fuzz复现

评估结果

我们分别记算了纯system-mode和增强进程模拟的首次触发crash的时间并将其记录在表6的最后两列中;通过对比时间不难发现Firm-AFL发现crash的速度比纯system-mode至少快3.6倍,在某些情况下甚至快10倍。
我们还在图7中绘制了Firm-AFL(蓝色)和纯system-mode(红色)在相同时间内所发现的crash的数量;图中的实线代表10轮后的中值,而虚线代表理想状态的上限和下限。由于表6中的后5个案例属于同一程序,结果相似,所以我们仅以CVE-2018-10749为代表进行绘制。
从结果可得,尽管fuzz过程中存在变数,但是Firm-AFL仍然能够发现更多crash,并且比纯system-mode快许多倍。我们进一步调查了这些crash,除了大多数由相同的1day漏洞引起的crash以外,我们还发现了两个0day漏洞,我们将在下文中进行描述。
图7:发现crash的机率和时间的关系

0day漏洞

Firm-AFL执行分别在7.5小时和6小时后发现了这两个0day漏洞。我们还尝试了使用相同地初始样本,用纯system-mode来进行fuzz,结果是纯system-mode无法在24小时内发现这两个漏洞。我们向IOT设备制造商和MITRE公司报告了这两个漏洞;下面将详细描述这两个漏洞。
CVE-2019-11417:栈溢出,固件名称:TrendnetTCUP110WN(固件版本:v.1.2.2 build 68)。攻击者可以利用系统中的system.cgi程序中的“languse”参数来攻击设备。
CVE-2019-11418:栈溢出,固件名称:Trendnet TEW-632BRP(固件版本:v.1.010B32)。攻击者可以通过构建soapaction HNAP接口来攻击设备。

六、讨论

本节我们将讨论系统中的局限性,并为未来的工作提供一些思路。

CPU架构的兼容性

目前Firm-AFL兼容的CPU架构:mipsel,mipseb和armel,这些架构包括了Firmadyne中90.2%的固件镜像。因为qemu中大多数模拟逻辑是以一种独立于架构的方式实现的,所以兼容更多的CPU架构并不算太难。

IOT固件兼容性

即使兼容再多的CPU架构,Firm-AFL也只能在Firmadyne中能正确模拟并运行posix兼容的操作系统(例如Linux)的IOT固件中做fuzz;这是Firm-AFL的设计缺陷,因此没有简单的解决办法。IOT固件模拟的改进与本文的研究是正交的;我们将在以后的工作中进行改进。支持非POSIX程序需要虚拟化,这样它们才能在POSIX进程中正常运行。对此我们还没有现成的解决方案。但是我们很乐于做出特殊的优化。

七、相关工作

随着IOT设备数量的不断增加以及安全问题的日益突出,提出了几种自动挖掘IOT设备漏洞的技术;这些技术可以分为静态分析和动态分析。由于缺乏大量固件的源代码,静态分析往往依赖于二进制镜像和逆向工程技术。

静态分析

Constin et al.通过对文件和模块的粒度比较,对IOT固件进行了大规模的分析;他们的方法能够在不同供应商使用的公共第三方项目中找到许多已知的bug。Cojocar et al.提出了另一种从IOT固件中启发式识别解析器和复杂处理逻辑的方法,它们发现了几个漏洞。也就是说,这些方法存在很高的误报率,而且无法挖掘到0day漏洞。Feng et al.提出一种针对固件的跨平台bug搜索技术,该技术基于高级数值特征比较,平均只需要0.1秒就可以完成154个漏洞的搜索。Xu et al.做出进一步改进并提出了一种基于神经网络的跨平台二进制码相似性检测方法,该算法可以显著减少训练时间和特征向量生成时间,提高搜索精度。
Firmalice是另一个采用静态分析技术的二进制分析框架。Firmalice在固件二进制文件上使用符号执行,并使用反向切片技术,这使得漏洞分析变得更简单;Firmalice只关注基于分析师的一个程序的一个切片;该规范提供了有关特权程序代码,隔离潜在的危险代码;Firmalice使得分析具有可扩展性,同时也能够发现新的漏洞;话虽如此,但是Firmalice只能找到身份验证漏洞,并且要依赖于手动切片规范的分析。

动态分析

另一方面,动态分析IOT固件需要真实设备或者某种模拟。黑盒fuzz是一种通过直接与设备交互来发现漏洞的常用方法。最近,一些著作已经为真实设备开发了动态模拟器。例如:Zaddach et al.采取将硬件请求从模拟器重定向到IOT设备的方法开发了一个动态分析框架。在此基础上,Marius et al.开发了一个动态多目标编排框架,可以实现不同动态二进制分析框架、调试器、模拟器和真实物理设备之间的互操作。然而,大量的硬件限制了它的可伸缩性,也带来了巨大的开销。
Chen et al.提出了一种基于软件的完整系统模拟,他们的模拟是基于内核检测的,他们的目标是执行自动漏洞验证,并不具备挖掘0day的能力。Avatar和Firmadyne都没有使用fuzz等能够在真实情况下挖掘0day的技术,Anderi et al.对IOT固件镜像进行了动态分析以实现全自动漏洞挖掘;该工具旨在通过web渗透工具发现与web界面相关的漏洞,但是却无法挖掘到IOT固件中其他模块的漏洞。

IOT fuzz

对于IOT fuzz,Muench et al.开发了六种启发式实时分析方法,包括调用堆栈跟踪和调用帧跟踪;Muench et al.在Avatar和PANDA的基础上构建了他们的系统,他们的系统可以有效的检测很多设备的内存损坏;然而该系统将目标系统作为黑盒并从外部输入,这将增加设备启动和fuzz会话重启的开销。此外,与灰盒fuzz不同,探索输入空间是盲目的,因此发现bug的概率会非常低;我们在工作中使用使用灰盒fuzz并尽量减少fuzz迭代时的开销,以便提高fuzz的效率;此外,Alimi et al.提出了使用fuzz技术和特定模拟器(JCOP)来发现托管在智能卡中的程序的漏洞,但由于各种IOT固件模拟的问题该方法无法扩展。

八、结论

基于代码覆盖率的灰盒fuzz已被证明是在真实程序中找到漏洞的有效方法。然而,有两个技术难题导致灰盒fuzz无法应用在IOT固件上;首先,最先进的灰盒fuzz,例如AFL,由于特定的硬件依赖,无法同时运行许多IOT程序。其次,能解决第一个问题的方案(例如:纯system-mode)的吞吐量非常低。我们提出了一种新技术:增强进程模拟;以同时解决这两个难题。增强进程模拟通过在user-mode中运行目标程序,并在目标程序调用具有特定硬件依赖的系统调用时切换到system-mode来提高吞吐量。
我们评估了Firm-AFL的透明度和效率;结果表明,我们的系统的吞吐量比目前已有的所有最先进的IOT固件fuzz高出一个数量级。我们的案例研究进一步表明,Firm-AFL确实可以比纯system-mode更快的找到1day漏洞,并且能够在一台机器上仅两个小时内找到两个0day漏洞。

- End -

看雪ID:pureGavin

https://bbs.pediy.com/user-home-777502.htm

  *本文由看雪论坛 pureGavin 翻译,转载请注明来自看雪社区。

# 往期推荐

公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458382053&idx=1&sn=b473d94eee77e9e3569546b40d5014d1&chksm=b180de6f86f757798a88d0f59f459b0b3ae0a5c654c76e1d7d0c04525a2be9ae9b877b4839fa#rd
如有侵权请联系:admin#unsafe.sh