IOT环境搭建与固件分析
IOT 环境搭建
binwalk
binwalk
是 IOT 固件分析中用来解压设备固件的常用工具,在 CTF 杂项中也常会用到binwalk
进行文件提取,但是现在将要对binwalk
进行更加深入的了解
binwalk
可以通过 apt
安装,也可以自己编译安装
建议尽量通过源码安装 binwalk
,因为 apt
安装的 binwalk
可能不完整,后续遇到各种报错问题需要单独去解决,将缺少的东西安装回来
安装 binwalk
apt
安装binwalk
:
sudo apt install binwalk
在 Kali Linux 2024.1 中使用 apt
安装的是 binwalk v2.3.3
,目前最新版为 binwalk v2.3.4
- 编译安装
binwalk
如果系统自带 apt
安装的 binwalk
,且版本比较老旧,可以先卸载:
# 查看是否安装 binwalk
binwalk
# 如果已经安装 binwalk,首先卸载 binwalk
sudo apt remove binwalk
# 更新软件列表
sudo apt update
编译安装:
sudo git clone https://github.com/devttys0/binwalk.git /opt/binwalk
cd /opt/binwalk
sudo python3 setup.py install
# 测试安装
binwalk
安装 binwalk
运行过程中需要调用的命令行工具:
# binwalk 运行时,它依赖许多命令行工具来提取固件
sudo apt install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsswap squashfs-tools sleuthkit default-jdk lzop srecord
# 安装 C/C++ 编译器、liblzma、liblzo 和 zlib 依赖项
sudo apt install build-essential liblzma-dev liblzo2-dev zlib1g-dev
安装 sasquatch
用于分离
squashfs
固件系统,解开非标准的squashfs
文件系统
如果没有安装 sasquatch
,使用 binwalk -Me
提取固件的过程中会报如下警告:
WARNING: Extractor.execute failed to run external extractor 'sasquatch -p 1 -le -d 'squashfs-root' '%e'': [Errno 2] No such file or directory: 'sasquatch', 'sasquatch -p 1 -le -d 'squashfs-root' '%e'' might not be installed correctly
安装 sasquatch
:
sudo apt install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools
sudo git clone https://github.com/devttys0/sasquatch.git /opt/sasquatch
cd /opt/sasquatch
sudo chmod +x build.sh
sudo ./build.sh
通过 sudo ./build.sh
编译时可能会出现报错:
unsquashfs.c: In function ‘read_super’:
unsquashfs.c:1835:5: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation]
1835 | if(swap)
| ^~
unsquashfs.c:1841:9: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘if’
1841 | read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
| ^~~~~~~~~~~~~
cc1: all warnings being treated as errors
make: *** [<builtin>: unsquashfs.o] Error 1
错误原因是代码中的
if
出现了问题,报错提示在第1835
行出现了错误该错误是因为
if
条件语句缺少必要的左右花括号{
和}
,导致后面的'ERROR'
语句无法被包含在条件语句块中执行
解决方法参考:关于Kali编译sasquatch时出现 ‘if’ clause does not guard… -Werror=misleading-indentation 的解决方案-CSDN博客
使用 Fixed gcc build errors by threadexio · Pull Request #47 · devttys0/sasquatch 该 pull 中的一个 patch
文件来进行补丁:
cd /opt/sasquatch
sudo wget https://github.com/devttys0/sasquatch/pull/47.patch
sudo patch -p1 < 47.patch
sudo ./build.sh
再次编译后不会再报错,可以正常分离 squashfs
固件系统
安装 ubi_reader
用于分离
ubifs
固件系统,即:binwalk
分离后的后缀为ubi
的xxx.ubi
文件
如果 binwalk
解压后只有 xxx.ubi
文件而没有文件系统,或者 binwalk
执行 ubireader_extract_files
程序失败:
MARNING: Extractor.execute failed to run external extractor 'ubireader_extract_files -o 'ubifs-root' "%e": [Errno 2] No such file or directory
这是因为 binwalk
缺少必要的组件:ubi_reader
注意:
ubi_reader
这个工具很重要,如果路由器固件是ubi
格式,需要用ubireader_extract_files
来提取,否则会无法解压出文件系统
参考文档:https://blog.csdn.net/gybwq/article/details/113850747
安装 ubi_reader
:
sudo apt install liblzo2-dev
sudo pip install python-lzo
sudo pip install ubi_reader
ubi_reader
工具提供了四个脚本:
- 获取 UBI 信息、布局块等:
ubireader_display_info
- 提取镜像:
ubireader_extract_images
- 提取文件内容:
ubireader_extract_files
- 分析 UBI 镜像并创建 shell 脚本和 UBI 配置文件:
bireader_utils_info
ubi_reader
工具的使用也很简单,可以不需要参数:
# 提取镜像里面的文件,输出保存到 ./ubifs-root/ 目录
ubireader_extract_files rootfs.ubi
QEMU
VMware 和 Virtualbox 之类通常只能在 x86 计算机上虚拟出一个 x86 虚拟机,而 QEMU 支持在 x86 上虚拟出一个 ARM 虚拟机
QEMU 源码下载地址:Index of / qemu
虚拟机磁盘镜像下载地址:Index of /~aurel32/qemu
安装 QEMU
- 通过
apt
安装 QEMU:
sudo apt install qemu qemu-kvm virt-manager bridge-utils binfmt-support
sudo apt install qemu-system qemu-user-static # 安装系统态、用户态
# 安装依赖库
sudo apt install -y gcc-arm-linux-gnueabi
sudo apt install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential gdb-arm-none-eabi synaptic gcc-aarch64-linux-gnu eclipse-cdt git
注意:
Ubuntu 24.04 这种新版本系统中貌似去除了
sudo apt install qemu
,但仍可以通过sudo apt install qemu-system qemu-user-static
安装,只不过版本是qemu-8.2.2
,不是最新版QEMU 有 user mode 和 system mode 两种配置方式
其中 QEMU 在 system mode 配置下模拟出整个计算机,可以在 QEMU 之上运行一个操作系统;而 user mode 仅可用来运行对应架构的二进制文件,例如:交叉编译
- 通过源码编译安装 QEMU
如果通过 apt
安装过 QEMU,首先卸载:
# 删除包和相关依赖
sudo apt remove --auto-remove qemu*
# 删除配置文件和相关的数据文件
sudo apt purge --auto-remove qemu*
采用源码编译安装 qemu-9.0.0
:
wget https://download.qemu.org/qemu-9.0.0.tar.xz
tar xvJf qemu-9.0.0.tar.xz
cd qemu-9.0.0
安装依赖:
sudo apt install ninja-build zlib1g zlib1g-dev libglib2.0-dev libpixman-1-dev libfdt-dev python3-venv libgtk-3-dev build-essential pkg-config binutils-dev
sudo apt install flex bison
编译安装:
sudo mkdir build && cd build
# 以编译 x86_64 架构的 QEMU 为例,-softmmu 表示 system mode
sudo ../configure --enable-kvm --target-list=x86_64-softmmu --enable-debug
# 启动多核心编译加快速度
sudo make -j$(nproc)
# 将其安装到 /bin 目录下,即可通过终端启动
sudo make install
编译命令中一些参数的说明:
参数 | 含义 |
---|---|
--enable-kvm | 表示开启 kvm 支持 |
--target-list | 指定要编译的 CPU 架构(如果不指定,就是全部架构都编译),其中 -softmmu 表示 system mode,-linux-user 表示 user mode |
--enable-debug | 能够对 QEMU 进行调试 |
目前在 Ubuntu 24.04 中编译 AARCH64 架构的时候会报错
[2181/9361] Compiling C object libqemu-aarch64-linux-user.fa.p/target_arm_helper.c.o ninja: build stopped: subcommand failed. make: *** [Makefile:167:run-ninja] 错误 1
解决方法:将报错文件
../target/arm/cpu.c
的第 1020 行的CS_ARCH_ARM64
改为CS_ARCH_ARM
,重新编译即可
编译完成后,会在 build
目录下生成 x86_64
架构下的 QEMU 本体:qemu-system_x86-64
在终端验证安装:
qemu-system_x86-64 --version
查看 QEMU 安装路径:
which qemu-system-x86_64
配置网络脚本
另外,QEMU 默认没有网络脚本文件,需要自己进行创建
在 /usr/local/etc
目录下(也有可能是 /etc
目录下,可以到时候根据报错路径来确定具体位置),新建 qemu-ifup
文件:
#!/bin/sh
set -x
switch=br0
if [ -n "$1" ];then
ip tuntap add $1 mode tap user `whoami`
ip link set $1 up
sleep 0.5s
ip link set $1 master $switch
exit 0
else
echo "Error: no interface specified"
exit 1
fi
增加权限:
sudo chmod 755 /usr/local/etc/qemu-ifup
GNS3
GNS3 是一种可以仿真复杂网络的图形化网络模拟器,GNS3 允许在计算机中运行 Cisco 的 IOS
GNS3 其实是 Dynagen 的图形化前端环境工具软件,而 Dynamips 是仿真 IOS 的核心程序,Dynagen 运行在 Dynamips 之上,目的是提供更友好的、基于文本的用户界面
注意:GNS3 仅支持 Cisco 设备的仿真
Windows 安装 GNS3
首先下载安装 GNS3:Software | GNS3(这是官网的最新版,当然也可以去第三方下载特定版本)
安装过程参考:网络工具之GNS3安装及使用_gns3的安装步骤及导入路由器镜像的过程-CSDN博客
安装成功后界面如下:
另外 GNS3 也有 VM 版本,同样在 Software | GNS3 处下载(这是官网的最新版,当然也可以去第三方下载特定版本)
下载解压后名为 GNS3 VM.ova
,直接导入 VMware Workstation 即可:(注意 GNS3 的版本需要与 GNS3 VM 的版本一致)
在物理机上测试一下是否能正常访问 Web-UI:
同时在 GNS3 中配置 GNS3 VM:
配置成功后在 Servers Summary 处会显示 GNS3 VM 并且为绿色:
GNS3 自带的终端为 Putty,不过 GNS3 支持与 SecureCRT 之类的终端仿真软件进行绑定
下载安装 SecureCRT(一款 SSH 终端仿真软件),网上教程很多,请自行下载 SecureCRT 并破解(仅用于学习交流,请支持正版):
在 GNS3 中将终端改为 SecureCRT,如果你的 SecureCRT 安装没有问题,会自动识别到其所在路径:
自行检查 GNS3 识别到的 SecureCRT 安装路径是否正确:
选择 Run appliances on my local coputer
:(如果安装了 GNS3 VM 也可以选 Run appliances in a virtual machine
将固件运行在 GNS3 VM 中)
初始化 GNS3 Server,保持默认即可(这里 Host binding
默认为 localhost
,但建议设置为 127.0.0.1
,这样当网络环境发生变化时,仍然能够连接到 GNS3 VM)
Linux 安装 GNS3
参考官方文档:GNS3 Linux Install | GNS3 Documentation
以 Kali Linux 为例:
sudo apt update
sudo apt install python3 python3-pip pipx python3-pyqt5 python3-pyqt5.qtwebsockets python3-pyqt5.qtsvg qemu-kvm qemu-utils libvirt-clients libvirt-daemon-system virtinst dynamips software-properties-common ca-certificates curl gnupg2
pipx install gns3-server
pipx install gns3-gui
pipx inject gns3-gui gns3-server PyQt5
gns3 # 启动 GNS3
固件分析
路由器固件分为加密固件和未加密固件,对于未加密的固件使用
binwalk
可以直接提取,而对于加密的固件则需要先想办法解密
未加密固件的分析
以下分析都是以 Cisco 的
RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img
固件为例
如果以下所有流程全部正常走完,那就说明我们的环境是没有问题的
固件扫描
使用 binwalk
扫描固件,可以查看固件信息:
binwalk 路由器固件(一般以 .bin 或 .img 为后缀)
固件提取
使用 binwalk
提取路由器固件:
binwlak -Me 路由器固件(一般以 .bin 或 .img 为后缀)
binwalk
提取后,会在固件所在目录得到一个 _XXXXX.extracted
文件夹,其中通常有一个 _fw.gz.extracted
文件夹
在形如 _openwrt-comcerto2000-hgw-rootfs-ubi_nand.img.extracted
的文件夹中有一个 ubifs-root
文件夹
**如果通过
binwalk -Me
解压后没有ubifs-root
文件夹,只有一个0.ubi
文件,说明没有安装ubi_reader
**,请参照上面的环境搭建一节自行安装
其中存放着路由器文件系统的根目录 rootfs
,其结构如下:
注意:如果上图中出现 var -> /dev/null
,说明是有问题的
仔细看 binwalk
的提取信息,会发现很多如下警告:
WARNING: Symlink points outside of the extraction directory: /home/wyy/IOT/Cisco/RV34X-v1.0.03.29-2022-10-17-13-45-34-PM/_RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img.extracted/_40.extracted/_fw.gz.extracted/_0.extracted/_openwrt-comcerto2000-hgw-rootfs-ubi_nand.img.extracted/ubifs-root/790534924/rootfs/www/index.html -> /tmp/www/index.html; changing link target to /dev/null for security purposes.
意思是:原本文件中存在的软链接指向了提取目录之外
就比如当前的 var
目录,它指向的是 Kali Linux 本机的 /tmp
目录(实际上应该指向路由器的 /tmp
目录,而不是本机的 /tmp
目录),为了安全考虑,binwalk
将这种软链接都置成了 /dev/null
这里如果放任不管,后面进行路由器的仿真会失败,比如路由器的某个服务需要去访问
var
目录下的文件,但它如果被置成/dev/null
的话,目录自然是缺失的
解决方法是找到 binwalk
安装路径下的 /modules
文件夹,修改其中的 extractor.py
文件
如果是通过 apt
安装的 binwalk
,不知道安装路径在哪里,使用如下命令搜索:
sudo find / -name binwalk
找到 extractor.py
文件后,搜索:"os.devnull"
,大概在文件的最末尾,1008 行,将 if not ...
改为 if 0 and not ...
然后使用 binwalk
重新解压固件,即可得到 var -> /tmp
的文件系统(如果是自行编译安装的 binwalk
,可能需要首先在 binwalk
安装根目录下使用 sudo python3 setup.py install
重新安装一下再解压):
到这里就提取完毕了
补充另一种方法,安装
ubi_reader
后通过ubireader_extract_files
工具单独对0.ubi
进行提取也可以避免上述软链接的问题:ubireader_extract_files 0.ubi
查看路由器属性
查看路由器属性(为后续配置搜集信息)
在路由器文件系统的 /bin
目录下,有一个 busybox
的二进制可执行文件:
cd /bin
file busybox
可见该路由器是 32 位 ARM 架构,小端序,动态链接
那么进行路由器仿真时就需要使用 32 位 ARM 架构的 QEMU
QEMU 运行异架构程序
在 x86 架构的 Kali Linux 下模拟运行 ARM 结构下的 busybox
,测试一下 QEMU 是否正常
由于我们是在 x86 架构上运行 ARM 架构的 32 位二进制可执行程序,因此使用 qemu-arm-static
来运行
因为我们是运行二进制程序,因此使用 QEMU 用户态进行模拟,也就是带
static
的版本还有一种带
system
的版本,是用于模拟系统级的 QEMU,其包括 CPU、内存、外设、操作系统等,能模拟出一个完整的操作系统环境
查看是否安装了所需的 QEMU 版本:(如果是自己编译安装的 QEMU,可能在 /usr/local/bin
路径下)
ls /usr/bin/qemu-arm*
注意:
必须根据文件的架构,使用相应架构的 QEMU 模块运行,不然会报
Invalid ELF image for this architecture
这个错误
首先将 qemu-arm-static
移动到固件目录,然后给予 busybox
执行权限,使用 QEMU 运行该 ARM 架构的二进制程序
sudo cp /usr/bin/qemu-arm-static ./
chmod +x ./bin/busybox
sudo chroot . ./qemu-arm-static ./bin/busybox
输出以下信息则说明运行成功:
加密固件的分析
厂商为了保护自己的产品,增加逆向分析的成本,通常会将路由器的固件进行加密,经过加密的固件使用一般的解包方法不能提取。要想分析这种固件,首先就要找到其对应的加密方法
实际上固件加密并不是随随便便拿来一种算法就可以的,由于家用路由器机能有限,所以 RSA 等效率较低的加密体系用的比较少,AES 或者 RC4 这些效率高的算法通常是首选的固件加密手段
加密场景
一般常见的路由器加密场景有:
- 固件的初始版本未加密,后续某个版本加密了,在加密版本与初始版本中间的某个版本附带了解密程序
我们可以通过获取附带了解密程序的中间版本,分析其解密程序,然后用于固件解密
- 固件的老版本有加密,但是后续更换了加密方式,中间版本发布了未加密的过渡版本固件
我们可以通过获取带有解密程序的过渡版本固件分析,提取解密程序,然后用于固件解密
- 固件的老版本有加密,但是后续更换了加密方式,中间版本更换了新的未加密的解密程序
如果清楚早期加密方式,或者拥有早期解密程序,可以去分析更换解密程序的中间版本,来获取解密程序
如果没有早期相关解密信息,则更多是购买设备,从硬件直接提取未加密的固件
理论上也可以使用二进制对比分析工具,来分析尝试提取复原解密程序
判断方法
参考文章:
通过信息熵分析
熵泛指某些物质系统状态的一种量度,某些物质系统状态可能出现的程度
熵值越小,说明重复内容越多,系统越不稳定;熵值越大,说明信息中重复的内容越少,系统越稳定
对于没有加密的二进制文件来说,一些指令出现的频率通常很高,并且数据结构几乎没有随机性,所以熵值一般比较低
对于经过加密的二进制文件来说,都会想尽办法隐藏自己的信息,而导致很少有重复的内容,所以熵值一般都会比较高
提前安装 Matplotlib
库:
pip install Matplotlib
查看固件的信息熵:
binwalk -E 路由器固件(一般以 .bin 或 .img 为后缀)
以 D-Link 的 DIR_878_FW1.30B08.bin
固件为例,该固件是被加密过的:
可以看到其熵值几乎稳定在 1.0,符合加密的特点
经过我对多个固件的多次测试,发现通过熵值来判断固件是否加密不是特别可靠,仅仅只能作为一个参考
比如,以下是未加密的固件
RV34X-v1.0.03.29-2022-10-17-13-45-34-PM.img
熵值:但我发现,未加密的固件熵值都是稳定的一条线,反而加密的固件最开始会有一段小波动,也许可以作为判断依据?
通过十六进制数据分析
使用 010 Editor 等十六进制查看工具打开固件,判断其中是否包含 0xFF
或 0x00
字节、是否为随机的数据
以 D-Link 的 DIR_878_FW1.30B08.bin
固件为例,该固件是被加密过的:
其中间版本固件 DIR878A1_FW104B05_Middle_FW_Unencrypt.bin
是未加密的:
可以看到加密的固件中间出现了大面积的 0x00
断层,并且几乎不存在有意义的字符
而未加密的固件开头是存在固件的设备型号和系统等信息的
通过 binwalk 分析
通过 binwalk
直接分析固件也是可以判断固件是否被加密的:
binwalk 路由器固件(一般以 .bin 或 .img 为后缀)
未加密的固件:
加密的固件:
可见被加密的固件无法被 binwalk
分析出任何信息
固件解密
以 D-Link 的
DIR_878_FW1.30B08.bin
固件为例
我们通过在 D-Link 官网找到该固件:D-Link Technical Support
发现该设备为 DIR-878,其存在很多历史版本:
将固件所有的历史版本下载进行查看:
在中间版本 DIR-878_REVA_FIRMWARE_v1.10B05
文件夹中,有一个名为 DIR878A1_FW104B05_Middle_FW_Unencrypt.bin
的固件,这就是我们前面提到的未加密的中间版本了
通过 binwalk
提取固件系统:
在 /bin
目录下存在一个名为 imgdecrypt
的二进制程序:
尝试通过 QEMU 运行它,由于该程序架构是 MIPS32 小端序,我们使用 qemu-mipsel-static
来模拟:
根据输出信息,是需要传入一个参数 <sourceFile>
尝试用这个程序为 DIR_878_FW1.30B08.bin
固件解密,将参数 <sourceFile>
设置为加密的固件:
执行后程序输出了 key:C05FBF1936C99429CE2A0781F08D6AD8
但是发现这样解密失败,仍然无法解压固件,貌似必须将加密固件放在文件系统内
我这里放在 /tmp
目录下,然后再次运行 imgdecrypt
程序,直接使用 binwalk
分析 /tmp
目录下的固件会报权限错误,需要使用 sudo binwalk
可以看到 binwalk
已经可以正常分析被加密的固件,说明我们解密成功:
然后我们来看看 imgdecrypt
这个二进制程序,使用 IDA 7.7 打开:(IDA 7.0 以上版本自带 MIPS 架构反编译插件,如果没有,也可以自己安装 RetDec 插件:avast/retdec-idaplugin: RetDec plugin for IDA)
主函数如下:
主要解密逻辑主要涉及到 AES
、RSA
和 sha512
: