IoT Firmware Emulation and Exploitation 想起来什么写点什么

date
Sep 27, 2020
URL
slug
iot-record-idea
status
Published
tags
固件研究
summary
想起来什么写什么 也没空整理想法了。。
type
Post
 
自动化仿真firmware
 
这个感觉干了我也想干的事2333

qemu仿真

用docker直接起环境
可以解决配网卡的问题
应该可以方便很多
 
qemu-user模式
qemu-system模式
分别能做到什么
 
user模式只是单独把一个程序跑起来
system模式可以仿真整个环境
 
 
 

一些想法

假设想实现的目的是实现对固件进行fuzz+漏洞扫描
究极想法是实现"自动化"
需要解决的问题有:
1.针对众多的固件如何实现固件的自动化解压解密?
2.针对脚本语言可以做源码审计,针对web端可做api
 
IOTFUZZER:Discovering Memory Corruptions in IoT Through App-based Fuzzing
emmmmm通过app的接口去fuzz 就是盲爆破呗。
 
 
 
FIRMCORN 这个的思路不错 值得学习
用unicorn去运行 fuzz unicorn可以直接用python就能直接调
 
 
 

配置网卡

 
 
tunctl -t tap0  虚拟一个网卡
ifconfig tap0 192.168.2.1/24  把这个网卡设置到这个网段
 
firmadyne 模拟失败的原因:
1)与引导相关的问题,如引导顺序不正确或缺少文件;
2)与网络相关的问题,如网络接口不匹配或配置不当;
3)与非易失性RAM(NVRAM)相关的问题,如缺少库函数或自定义格式;
4)与内核相关的问题,如不支持硬件或函数;
5)小问题,如不支持命令或定时问题。
 
 

对于固件的fuzz可行的点在哪里

如果一个二进制程序存在漏洞 但只能在路由器自身使用时才会执行 通过外部交互无法触及是不没有用。。
是否应只针对如httpd upnp 等能进行通信交互的服务进行fuzz
协议fuzz?
 
对web层面进行fuzz,需要登录才能访问大多数的服务需要怎么做 默认密码登录然后用cookie? 这个好像好解决
web的扫描器有用没用。。
对于cgi 用ida脚本扫描可行性高还是进行对web的各个参数进行测试可行性高
 
 
 
notion image
 
 
TriforceAFL: AFL fuzzing with full-system emulation
decaf
firmafl
firmcorn
Marius Muench, Jan Stijohann, Frank Kargl, Aurélien Francillon, and Davide Balzarotti. 2018. What You Corrupt Is Not What You Crash: Challenges in Fuzzing Embedded Devices. In Proceedings of the 2018 Annual Network and Distributed System Security Symposium (NDSS). San Diego, CA.
memory corruption
 

nvram仿真

大致思路:
运行httpd等服务会从硬件nvram中读取如绑定ip等信息,找到他的nvram_get函数,对这个函数进行重写,然后通过交叉编译到so文件中,运行httpd时,先指定LD_PRELOAD
 
#include <stdio.h>
#include <string.h>

char *nvram_get(char *key)
{
        char *value = NULL;

        if(strcmp(key, "lan_ipaddr") == 0)
        {
                value = strdup("127.0.0.1");
        }

        if(strcmp(key, "lan_proto") == 0)
        {
                value = strdup("static");
        }

        printf("nvram_get(%s) == %s\n", key, value);
        return value;
}
我们需要将此代码作为共享库进行交叉编译,并将其复制到squashfs-root目录中,这个目录是我们运行qemu的目录:
eve@eve:~$ arm-linux-gcc -shared nvram.c -o nvram.so
eve@eve:~$ cp nvram.so squashfs-root/nvram.so
现在,我们将尝试再次在Qemu中运行httpd,这一次在LD_PRELOAD环境变量中指定nvram.so文件的路径:
eve@eve:~/squashfs-root$ sudo chroot . ./qemu-arm -E LD_PRELOAD="/nvram.so" usr/sbin/httpd 
usr/sbin/httpd: relocation error: /nvram.so: symbol __register_frame_info, version GLIBC_2.0 not defined in file libgcc_s.so.1 with link time reference
看起来nvram.so文件期望引用__register_frame_info函数,该函数符号在目标系统的libgcc_s.so库中不存在。发生这种情况是因为我们用来构建nvram.so的工具链与供应商用来为目标系统构建固件的工具链不同。我们希望__register_frame_info存在,而他们却没有。

由于供应商未为其系统发布GPL代码,因此我们不能简单地使用其工具链来重新构建nvram.so。我们可以向公司发起GPL请求,但是有一种更简单(更快!)的方法。我们只需要在nvram.c中为__register_frame_info符号添加占位符定义(函数声明):
#include <stdio.h>
#include <string.h>

void __register_frame_info(void) { }
void __deregister_frame_info(void) { }
void __unregister_frame_info(void) { }

char *nvram_get(char *key)
{
        char *value = NULL;

        if(strcmp(key, "lan_ipaddr") == 0)
        {
                value = strdup("127.0.0.1");
        }

        if(strcmp(key, "lan_proto") == 0)
        {
                value = strdup("static");
        }

        printf("nvram_get(%s) == %s\n", key, value);
        return value;
}
 
 
 

firmae思路

对firmadyne进行改进

拿出一个很大的样本集来
批量通过firmadyne来进行仿真,将仿真失败的样例收集起来,分类,
统计下来仿真失败的主要点有以下几个:
  • boot
  • network
  • nvram
  • kernel
  • others
下面来看看它具体的分析步骤

boot

先分析 boot sequence boot顺序出错很大程度是因为系统初始化没有正常执行
一般来说,大多数系统在开机程序中都需要进行初始化。在Linux内核中,初始化通常由一个叫做init的程序来执行,内核试图通过检查预定义的路径来找到这个程序,比如/sbin/init、/etc/init和/bin/init。然而,有些固件镜像有自定义的初始化程序路径,这样内核就无法执行程序并崩溃。
这种故障经常发生在NETGEAR固件镜像中。经过分析,我们发现它们使用了一个开源的嵌入式设备项目OpenWrt[22]经常使用的名称preinit,我们验证了它们确实是在其上实现的。我们还发现,一些TP-Link图像也利用了preinit。为了解决这个问题,Firmadyne建立了一个脚本,它可以搜索并执行一个经常访问的硬编码文件列表,用于初始化程序。然而,这个列表并不能包含所有镜像的程序。
提出了另一种利用目标固件内核信息的方法。具体来说,我们在启动过程开始时创建了一个干预措施,它可以从镜像的内核中提取有用的信息。具体来说,我们利用了一个内核的命令行字符串,在启动过程中用于内核的默认配置。需要注意的是,这样的字符串是在开发阶段预先定义好的,所以它自然而然地被嵌入到内核映像中。这些信息可能包括初始化程序路径、控制台类型、根目录、根文件系统类型或内存大小。例如,从NETGEAR固件的一个内核映像中,我们可以得到一串 console=ttyS0,115200 root=31:08 rootfstype=squashfs init=/etc/preinit。我们可以认识到初始化程序路径为/etc/preinit,控制台类型为ttyS0,具有115200波特率,根文件系统类型为squashfs。通过用从原始内核获得的信息来配置仿真环境,即使初始化程序的路径不正常,客体系统也能正确初始化,不会出现故障。如果我们提取不到任何信息,我们就从提取的文件系统中找到preinitpreinitMT等初始化程序。
文件系统结构缺失。其他失败的情况是由于缺少文件或目录。当内部程序访问这些路径时,它们就会崩溃,仿真就会停止。Firmadyne试图通过在自定义启动脚本的开头创建和挂载硬编码路径来解决这个问题,如proc、dev、sys或root。一些硬编码路径当然有效;例如,制作/etc/TZ或/etc/hosts有助于解决这种故障的几个案例。然而,这种方法不能说明不同的情况。此外,由于它在固件初始化之前强行创建文件和目录,它与内部程序发生碰撞,后者在相同的路径中创建和挂载其他文件或目录。
我们通过插入一个干预措施来仲裁这个问题,这个干预措施与前面的情况类似,但是从文件系统而不是内核中检索信息。在模拟一个给定的图像之前,我们从其文件系统中的可执行二进制文件中提取所有字符串。然后,我们对它们进行过滤,以获得极有可能指示路径的字符串,并根据这些路径准备文件结构。特别是,我们选择了以一般Unix路径开头的字符串,如/var,或/etc。

网络

在完成开机程序后,应配置好网络,使主机系统能与客机系统进行通信,最终可以进行动态分析。为了实现网络通信,QEMU要求主机多建立一个网络接口TAP这个TAP接口与来宾系统的网络接口相连。然后,主机和来宾系统通过它进行通信。
然而,正确配置TAP接口并不是一件小事,因为它应该设置与目标网络接口类型相对应的特定选项。这个网络接口类型可以是以太网、无线局域网(WLAN)、网桥或虚拟局域网(VLAN)。由于在客体系统中静态地区分接口类型并不容易,因此需要对目标映像进行一次仿真。 Firmadyne对一个给定的映像进行两次仿真(§2.4)。在第一次仿真中,即预仿真,Firmadyne通过hook系统调用来收集内核日志。由于收集到的日志包括在仿真过程中访问的网络接口的名称和IP地址,它们可以在最后的仿真中用于网络配置。然而,众多图像还是出现了故障。
无效的IP别名处理。将多个IP地址分配到一个网络接口上称为IP别名[58]。它在路由器中很普遍,因为它可以按IP地址分别管理服务。在IP别名中,一个网络接口自成多个实例,每个实例被分配一个唯一的IP地址。例如,桥接接口br0的IP地址为192.168.1.1,可以有169.254.39.3和1.1.1.1的IP别名,分别分配给它的实例br0:0和br0:1。然后,br0链接到一个以太网接口eth0。在这里,可以用这些IP地址中的任何一个来访问br0。
在D-Link的图像中经常发现与这种IP别名有关的故障案例。经过调查,我们发现是由于Firmadyne没有正确处理IP别名造成的。该问题发生在主机系统中Firmadyne网络配置过程中。在pre-emulation步骤中,IP别名被内核记录下来。然后,Firmadyne解析日志,并尝试将所有记录的IP地址分配到客户机中的相应接口。然后,它为这些IP地址添加静态路由规则,将其链接到主机中的TAP接口。在这里,多个路由规则被添加到一个TAP接口上,这使得网络发生碰撞。
在了解了IP别名的情况下,FirmAE通过让主机系统使用其默认的路由规则来进行仲裁。特别是,即使使用了IP别名,一旦客人的网络接口与主机的TAP接口相连,所有的数据包都会自动在主机和客人之间进行路由选择。因此,这些情况下不需要干预,这说明了针对合适的情况放置干预的重要性。
没有网络信息。有些固件映像在其内核日志中不包含任何关于可连接网络接口的信息,例如eth。这些映像仅配置了环回接口(lo),而没有设置其他网络接口。由于缺乏可连接的网络接口,这些映像无法从主机系统中访问。此外,有些镜像试图将其网络服务器绑定到一个不存在的网络接口上,结果导致崩溃。 经过对这些案例的分析,我们发现有些图像使用动态主机配置协议(DHCP)从DHCP服务器上获取IP地址,用于其广域网接口。由于DHCP不需要用户进行任何交互,因此在终端设备中设置网络接口是一个很流行的协议。一般来说,无线路由器本身作为DHCP服务器,为其客户端连接的LAN接口分配IP地址。然而,它们也可以从外部DHCP服务器中检索IP地址来将其WAN接口连接到互联网,除非用户手动配置。事实上,我们的分析图像试图通过他们的WAN接口和主机系统的TAP接口之间的连接,用DHCP检索一个IP地址。然而,由于仿真环境中没有DHCP服务器,仿真固件无法获取IP地址并配置网络接口。此外,由于没有配置网络接口,无法安排将多个网络接口分组的桥接接口。因此,绑定在这些网络接口上的内部程序无法正常运行。 我们首先尝试用QEMU内部的DHCP服务器来解决这个问题,这样客人的网络接口可以从服务器上检索IP地址。但是,即使设置了DHCP服务器,仍有部分镜像没有网络接口。这可能是由于外围设备支持不足造成的。如果在网络配置过程中任何程序访问这些外设,就会崩溃或行为异常,最终导致网络配置失败。
FirmAE对这些情况进行仲裁,通过干预,用默认设置强行配置网络。具体来说,我们设置一个以太网接口eth0,IP地址为192.168.0.1。以太网接口设置好后,对于那些内核日志中包含桥接接口信息的镜像,会与默认的桥接接口br0链接。这个简单的干预大大有助于模拟网络服务(§5.1)。
ARM中的多个网络接口。为了支持多个网络接口,必须选择一个合适的机器,将目标固件加载到该机器上。我们按照之前的研究[17]中采用的方法,选择了Virt,这是QEMU支持的机器之一。这对于几个固件映像来说表现良好;然而,它无法模拟具有多个网络接口的ARM固件映像。Firmadyne试图通过准备一个固定数量(4个)的虚拟接口来解决这个多接口问题。它的基本假设是接口的数量应该大于或等于接口名称的后缀,而后缀是从内核日志中提取的。例如,如果eth1被记录下来,那么极有可能eth0也存在。然而,几乎所有的ARM映像仍然没有被仿真。
们仔细调查了这些情况,但我们无法确定确切的原因。尽管如此,我们可以通过一个高级干预措施来解决该故障,该措施只强制设置一个以太网接口。更具体地说,我们的干预措施强行设置了一个以太网接口eth0,而避免设置其他接口。因此,我们设置了一个桥接网络接口,并在必要时将其链接到主机上。通过这种干预,可以模拟很大一部分ARM固件镜像。 (arm太难了)
VLAN设置不足。VLAN是路由器的典型功能,因为它提供了一个隔离的网络环境,在逻辑上对子网络进行分组。VLAN接口与其他网络接口(如以太网或WLAN)相比,具有不同的特性,因此必须对其进行额外的选项设置。为了支持VLAN,应将TAP接口的类型设置为VLAN,并为其分配一个合适的VLAN id。
另一个故障发生在带有VLAN接口的固件镜像中。在仿真这些镜像时,即使以太网接口正确配置了独立的IP地址,但客人网络还是无法到达。Firmadyne试图通过在设置主机TAP接口时运行一个命令来解决这个问题;但是,他们的配置不足以处理这个问题。特别是VLAN的设置,应该将主机和访客网络用同一个VLAN id进行分组。然而,Firmadyne驳回了设置主机网络的要求。FirmAE通过正确配置VLAN进行仲裁。
iptables中的过滤规则。众多路由器通过设计设置防火墙,防止未经授权的远程访问。否则,攻击者可以访问管理界面。我们数据集中的一些固件镜像也通过使用iptables来实现这一策略。因此,客人内核会丢弃所有来自主机的数据包。我们在TP-Link中发现了大多数这样的情况,即使主机和客体网络配置正确,客体也无法到达。
这并不代表仿真失败,因为设置iptables可以模拟真实设备的原始行为。然而,这样的过滤阻止了对其潜在漏洞和威胁的分析。显然,在分析过程中发现的漏洞可能不会被远程利用。然而,许多设备所有者或管理员错误地改变了这些规则,使得设备可以被公开访问[14,15,51]。
FirmAE通过检查访客系统中的过滤规则,并在规则存在的情况下将其删除来进行仲裁。这可以简单地通过刷新iptables中的所有策略,并将默认策略设置为接受所有传入数据包来实现。然后,来宾网络就可以从主机上进行访问,并进行动态分析。
 

© Somet1mes 2021 - 2024