在本文中,我们将为读者详细介绍如何挖掘物联网设备中的复杂恶意软件。
这篇文章的动机之一,就是鼓励对这个话题感兴趣的其他研究人员加入进来,分享自己的想法和知识,建立更多的安全工具,以便更好地保护我们的智能设备。
研究背景
智能手表、智能家居设备甚至智能汽车……随着越来越多的联网设备加入物联网生态系统,确保其安全性的重要性已经变得不言而喻了。
众所周知,现在已经成为我们生活中不可分割的一部分的智能设备,在面对各种网络攻击时,还不是非常安全。实际上,针对物联网设备的恶意软件已经存在十多年了。其中,Hydra是第一个已知的自动化路由器恶意软件,于2008年以开源工具的形式被公之于众。不过,Hydra只是一个开源的路由器恶意软件原型。在Hydra问世之后不久,安全专家又发现了针对网络设备的在野恶意软件。此后,不同的僵尸网络家族相继出现并广为流传,包括Mirai、Hajime和Gafgyt等家族。
除了以上提到的恶意软件外,在物联网设备中使用的通信协议中也爆出了许多漏洞,比如Zigbee协议,攻击者可以将某一设备作为攻击目标,并在得手后向网络中的其他设备传播恶意软件,这种行为非常类似于计算机蠕虫。
在这项研究中,我们重点挖掘针对物联网设备的低层复杂攻击。准确来说,我们将特别关注物联网设备的固件,以挖掘后门植入、对引导过程的篡改以及对固件其他部分的恶意篡改。
下面,让我们先来介绍物联网设备固件的结构,以便更好地了解其中的各个组成部分。
IoT固件结构
无论物联网设备的CPU采用了哪种架构,启动过程由以下几个阶段组成:引导加载程序、内核和文件系统(如下图所示)。当物联网设备启动时,板载SoC(片上系统)上的ROM中的代码首先会将控制权转移到引导加载程序,之后,引导加载程序将加载内核,而内核则会挂载根文件系统。
引导加载程序、内核和文件系统也构成了典型的物联网固件的三个主要组成部分。
物联网设备的引导过程
物联网设备中使用的CPU架构有很多种。因此,为了分析和理解固件的不同组件,就需要对这些深入了解这些架构以及其指令集。在物联网设备中,最常见的CPU架构包括:
- ARM
- MIPS
- PowerPC
- SPARC
可能的攻击场景
了解了固件结构后,我们就可以考察攻击者在部署难以检测的隐形攻击时,会如何利用这些组件了。
引导加载程序是第一个获得系统控制权的组件。因此,以引导加载程序为目标的攻击,为攻击者提供了执行恶意任务的绝佳机会。这也意味着攻击可以在重启后保持持久性。
除此之外,攻击者还可以操纵内核模块。实际上,大多数物联网设备使用的都是Linux内核。就像开发者很容易从Linux内核中定制和选择他们需要的任何东西一样,一个能够设法访问和操纵设备固件的攻击者也可以添加或编辑内核模块。
下面,我们来考察文件系统。实际上,许多常用的文件系统都可以用于物联网设备。这些文件系统通常很容易被攻击者所利用。攻击者可以从固件中提取、解压缩并安装原始文件系统,添加恶意模块,然后使用通用实用程序再次对其进行压缩。例如,SquashFS是一个Linux的压缩文件系统,在物联网厂商中相当流行。通过Linux实用程序“squashfs”和“unsquashfs”,我们就可以非常轻松地挂载或解压SquashFS文件系统。
本研究所面临的挑战
获取固件
实际上,有多种获取固件的方法。在进行调查时,有时我们希望获得的固件属于规格完全相同的设备。同时,我们还希望通过某些特定方式将其部署在设备上。例如,您怀疑用于更新固件的网络存在安全问题,并且考虑到在供应商的服务器和设备之间进行过渡时固件存在被篡改的可能性,因此,您想调查更新的固件以验证其完整性。在另一个示例场景中,您的设备可能是从第三方供应商处购买的,并怀疑固件可能被动过手脚了。
除此之外,还有大量的物联网设备,其制造商根本就没有实现任何获取固件的方法,甚至根本没有考虑要更新固件。也就是说,这些固件从设备出厂之日开始,终生不变。
在这种情况下,获取这些固件的最可靠方法是从设备本身提取固件。
这里的主要挑战是,这个过程不仅需要特定领域的相关知识,还需要具备使用嵌入式系统的专业硬件/软件经验。如果要挖掘针对IoT设备的复杂攻击,这种方法也缺乏可扩展性。
在获得IoT固件的各种方法中,最简单的方法就是从设备制造商的网站下载固件。但是,并非所有制造商都在其网站上发布其固件。通常情况下,大部分IoT设备只能通过设备物理接口或用于管理设备的特定软件应用程序(例如移动应用程序)来进行更新。
当从供应商的网站下载固件时,一个常见的问题是,你可能无法找到特定设备型号的旧版本固件。我们也不要忘记,在许多情况下,发布的固件二进制文件是加密的,它们只能通过设备上安装的旧固件模块来进行解密。
了解固件
根据Wikipedia的说法,“固件是一种特殊的计算机软件,用于为设备的特定硬件提供底层控制。固件既可以为更复杂的设备软件提供标准化的操作环境(提高硬件的独立性),也可以为不太复杂的设备充当设备的完整操作系统,来提供所有的控制、监视和数据处理功能。”
尽管固件的主要组件几乎都是一样的,但固件并没有标准的架构。
固件的主要组件通常是引导加载程序、内核模块和文件系统组成。但是,在固件二进制文件中还可以找到许多其他组件,例如设备树、数字证书以及其他设备特有的资源和组件。
一旦从厂商网站上获取了固件的二进制文件后,我们就可以着手进行分析,并对其进行反汇编处理了。鉴于固件的特殊性,其分析过程不仅具有很大的挑战性,并且非常繁琐。要获取有关这些挑战及其解决方案的详细信息,请参阅“IoT固件分析”部分。
查找固件中的可疑元素
在固件的组件被提取后,您就可以开始寻找可疑的模块、代码片段或针对组件的恶意篡改了。
首先,我们可以根据一组YARA规则来扫描文件系统内容,并且,这些规则可以基于已知的IoT恶意软件或启发式规则。此外,我们还可以使用防病毒扫描程序来扫描经过解压缩处理的文件系统的内容。
同时,您还可以在文件系统中查找启动脚本。这些脚本中通常会包含每次系统启动时所加载的模块列表。而恶意模块的地址就很可能会被插入到这些脚本中。
在这里,我们可以利用Firmwalker工具来扫描解压缩的文件系统,查找潜在的易受攻击的文件。
Firmwalker的功能(https://craigsmith.net/Firmwalker/)
另一个需要研究的地方是引导加载程序组件,尽管这更具有挑战性。
在物联网设备中有很多常用的引导加载程序,其中U Boot是最常见的。实际上,由于U Boot具有高度的可定制性,这使得确定编译后的代码是否被操纵变得非常困难。另外,在不常见或自定义的引导加载程序中查找恶意修改将会更加繁琐。
IoT固件分析
目前,已经有许多开源和闭源工具可以帮助我们来分析固件。不过,实际工作中我们最好采用经验丰富的固件分析师所推荐的工具和技术组合。
下面,让我们从功能最为丰富的固件分析工具Binwalk开始介绍。Binwalk可以用来扫描固件二进制文件并查找已知的模式和签名。
该工具不仅拥有IoT设备各种引导加载器和文件系统的签名集合,还提供了用于普通加密和压缩算法的签名,以及用于解压缩和解码的相应例程。
除此之外,Binwalk还能够提取在固件二进制文件中发现的组件。
下面的截图显示了Binwalk扫描一个样本固件二进制文件后的输出结果:
Binwalk工具的扫描结果
在上图中,Binwalk已经找到并打印出了头部信息、引导加载程序、Linux内核以及文件系统。此外,还输出了从头部信息和组件本身提取出的元数据的相关细节信息,如每个组件的类型和大小、CRC校验和、重要地址、CPU架构、镜像名称等。现在,我们可以继续使用Binwalk来提取上述组件,或者根据Binwalk找到的起始偏移量手动计算其大小,并提取相关组件。
提取完固件的组成部分后,我们还可以继续提取、解压甚至挂载文件系统,以考察文件系统的内容。当然,我们也可以在反汇编器中查看引导加载程序的代码,或者通过调试器对其进行调试。
然而,固件分析并不总是那么简单。实际上,由于固件种类太过繁多,以至于理解其结构和提取组件通常是相当繁琐的。
下面,让我们仔细考察另一个样本固件,并尝试了解它的结构。
1. Binwalk firmware.bin
Binwalk的扫描结果中没有显示任何内容。这意味着Binwalk没有发现任何已知的签名。
Binwalk的扫描结果
在这种情况下,我们发现简单的Binwalk扫描没有太大的帮助。但是,请注意,我们还可以使用其他工具和技术来深入了解固件的结构。
2. File firmware.bin
下面让我们利用Linux系统的file实用程序来考察这个固件的二进制文件。
File实用工具的输出结果
File实用程序将该文件的类型显示为Targa图像数据。通过查看二进制文件的头部,并对Targa图像数据签名进行Google搜索,发现这显然是误报。
固件二进制文件的第一个字节的内容
之所以出现这种误报,是因为这个固件文件的第一个字节的内容0x01010000与Targa图像数据的签名相匹配,具体请看上面的截图。
3. Binwalk -E firmware.bin
现在,让我们开始使用Binwalk工具的另一个功能:检查固件二进制文件的熵。
使用“-E”命令选项运行Binwalk时,会输出固件二进制文件的熵图,以及一些额外的详细信息,如熵增和熵减的起始处的偏移量。
与熵相关的详细信息
熵图
熵值接近1的内容表示经过了压缩处理,而熵值较低的内容表示未压缩和未加密。从上面的截图可以看出,偏移量55296(0xD800)开始,熵值进入高值区。
此外,还有另一个工具可以用来对二进制文件进行可视化处理:binvis.io。通过它,我们可以通过两个并排的窗格来考察固件文件的内容,并将其可视化。对于文件的不同的部分,会根据熵值以不同的颜色加以显示。
由binvis.io创建的固件的可视化结果
4. Binwalk -A firmware.bin
此外,Binwalk还可以扫描二进制文件,以寻找常见的可执行操作码的签名。
在文件中发现的第一个函数序言
在文件中发现的最后一个函数序言
从上面的截图中我们可以看到,操作码签名检查结果其实是非常有用的! 首先,我们可以藉此判断出该固件属于ARM设备。
其次,如果我们考虑到第一个和最后一个函数序言签名的偏移量,我们可以得到这样一个结论,即这些偏移量指向固件二进制文件中存放代码的段(sections)。
从截图中我们还可以看到,最后一个函数是在地址0xD600处找到的,也就是在熵上升的部分之前的0x200字节处。由此,我们可以进行一个有根据的猜测:这个偏移量很可能指向引导加载程序代码的结束位置,同时,也是压缩后的内核模块的开始位置。
5. Hexdump -C
- hexdump -C firmware.bin | grep -C 4 -e “^\*$”
现在,我们知道了固件文件中一些组件的大致边界,接下来,我们就可以通过查看这些区域周围固件文件的实际内容来确认这些边界的具体偏移量了。
如果我们通过hexdump来处理固件文件,并寻找只包含星号 "*"的代码行,我们就可以找到每个固件组件的编译器添加的填充字节。
固件二进制文件的内容
固件二进制文件其他部分的内容
通过Hexdump实用程序的输出内容,加上之前的发现,我们就能确认该固件二进制文件中包含ARM代码的部分。我们之前曾怀疑这段代码属于引导加载程序。
6. Strings –radix=x firmware.bin
接下来,让我们从固件中提取ASCII字符串以及相关的偏移量。
最后在固件二进制文件中找到的ASCII码字符串
从上面的截图来看,其中有一些是与模块入口点相关的字符串。这些字符串可以帮助我们更好地了解相关代码的性质。
在下面的截图中,我们可以从固件二进制的开头部分看到一些让人感兴趣的字符串。例如,库名“MctlApplet.cpp”可以用来查找来自同一开发商的其他二进制文件或软件包。拥有来自同一厂商的其他固件镜像有助于更好地理解这些二进制文件的结构。
从同一截图中,另一个引起我们关注的字符串是“Not Booting from softloader”,它可能用于表示进程状态,或这个模块的性质。
同时,含有“Assert()”的字符串也可能暗示代码的不同信息。使用Asserts函数是固件开发中非常常见的一种做法,因为它可以帮助开发人员在开发和生产阶段调试和排除代码中的故障。
在固件二进制文件中找到的第一个ASCII字符串
7. IDA -parm firmware.bin
到目前为止,我们已经从这个固件二进制中收集到了很多有价值的信息,而这些信息在一开始看来是相当难以理解的。
现在,让我们使用IDA来考察这些代码。由于这个二进制文件不是一个具有标准头部的ELF文件,因此,我们需要显式地告诉IDA使用ARM指令集来反汇编代码。
IDA中函数部分的反汇编视图
上面来自IDA的屏幕截图显示了如何使用在前面的分析步骤中找到的字符串来帮助查找对内核模块入口点的调用。
8. dd
现在,我们可以继续提取固件二进制代码部分,我们分析后发现该部分属于引导加载程序模块。
9. Qemu
从固件二进制文件中提取所有模块(文件系统内容、内核模块和其他组件)之后,我们就可以使用Qemu来运行这些二进制文件了,甚至可以模拟运行与我们自己机器的体系结构不同的文件,并与它们进行交互。
小结
如今,物联网设备的数量正在与日俱增。从工业控制系统、智慧城市、汽车到消费级设备,如手机、网络设备、个人助理、智能手表以及种类繁多的智能家电等,到处都可以看到物联网设备的身影。
物联网设备源自于已经存在多年的嵌入式系统。由于这些设备的自身特性的缘故,嵌入式设备软件的制造和开发一直有着不同于通用计算机系统的优先考虑事项。这些优先事项是由设备本身的有限和特定功能、底层硬件的有限能力和容量以及所开发的代码无法进行后续更改和修改所决定的。同时,物联网设备与传统的嵌入式系统还是具有显著的差异的。如今,大多数物联网设备都在具有与通用计算机系统相似功能的硬件上运行。
随着物联网设备变得越来越普遍,它们现在正在访问和控制我们生活和日常互动的许多方面。物联网设备现在有可能给攻击者带来前所未有的入侵机会。这不仅凸显了物联网设备安全的重要性,也彰显了围绕这一主题进行研究的现实意义。不过,好消息是,目前已经有许多工具和技术可用于协助安全研究人员进行该领域当前和未来的相关研究。掌握物联网设备的架构,学习这些设备所用的语言,以及坚定的决心和毅力是进入这个研究领域所必需的。