固件安全
背景
在实际的攻击场景中,攻击者很难获取到系统的shell(不排除一些远程连接的弱密码),所以除了利用一些系统级的安全漏洞,在拿到硬件后对IOT设备尝试进行固件攻击也是常见的攻击手段,所以固件安全也是IOT设备安全性设备的重要一环。
固件(firmware)其实就是写入硬件设备的软件,一般存储于设备中的电可擦除只读存储器EEPROM(Electrically Erasable Programmable ROM)或FLASH芯片中,不会存储在RAM中,作用是对应用和系统功能实时控制。固件中包含底层代码,这些代码能实现软件对硬件的操作。运行固件的设备称为嵌入式系统,嵌入式系统的硬件资源在存储能力以及内存等方面往往具有诸多限制。举例来说,智能手机、智能终端、交通信号灯、无人机、机顶盒都是运行固件的嵌入式设备。
信息搜集
可搜集与固件相关如下基础信息:
- 基于的CPU架构
- 操作系统平台
- 引导程序配置
- 硬件原理图
- 数据表
- 代码行估计
- 源代码存储库位置
- 第三方组建
- 变更日志
- FCC ID
- 设计和数据流程图
- 威胁建模
- 渗透测试报告之类
- 一些测试平台的测试(Jira、漏洞赏金平台 bugcrowd 或 hackerone )
信息搜集是进行渗透测试或者漏洞挖掘中最重要的一环
获取如上信息后便可进行粗略的威胁建模:标识出可攻击功能点和影响范围,方便测试时进行漏洞点的贯穿使用。
获取固件
- 可能最简单也是最难的的方法:直接从开发团队或者制造商/供应商或用户获取
- 从共享平台( Dropbox、box、Google drive)根据二进制文件扩展名获取
- 设备更新进行中间人(MITM)获取
- 云提供商存储位置(如:AWS)下载构建版本
- 通过 UART、JTAG、PICit等直接从硬件中提取
- 嗅探“硬件组件中的串行通信”中的更新服务器请求
- 通过移动应用程序中的硬编码接口
- 将固件从引导加载程序(如:U-boot )转储到闪存或通过tftp的网络转储
- 从主板卸下闪存芯片(如:SPI )或 MCU,以进行离线分析和数据提取
分析固件
通常固件文件以bin、zip、LZMA、arj等文件压缩类型封装,其中,最常见的为bin和zip格式,bin文件是二进制镜像形式,需要使用binwalk/unyaffs等工具提取squashsf文件系统。
通过对bin文件的提取,获得了后缀为squashfs的固件文件,该文件需要进一步解压,从而获得固件的根文件
squashsf文件系统的头部特征已知的有七种:标准固件大端sqsh、标准固件小端hsqs、LZMA压缩的大端sqlz、squashfs3.3在LZMA压缩下的大端qshs部分非标准小端shsq(D-LINK固件)、DD-WRT固件小端hsqt、DD-WRT固件大端tqsh
在提取固件的过程中,有可能遇到加密的压缩包,无法直接解压缩,这是厂商对固件做了保护,防止逆向分析,使用尝试frackzip工具可以破解zip密码
一般获取固件后需要分析其特征信息:固件文件类型、潜在的根文件元数据、编译基于的平台,使用 binutils 分析过程如下:
file <bin>
strings
strings -n5 <bin>
binwalk <bin>
hexdump -C -n 512 <bin> > hexdump.out
hexdump -C <bin> | head # might find signatures in header
若使用上述方法未提取出有用信息,可能由于以下原因:
- 二进制文件可能是Bare Metal
- 二进制文件可能仅适用于带有自定义文件系统的实时操作系统( RTOS )平台
- 二进制文件可能是加密的
判断二进制文件是否是加密:
- 通过命令:
binwalk -E <bin>
,判断其熵- 低熵:不太可能被加密
- 高熵:可能被加密(或以某种方式压缩)
- 也可以使用其他程序或在线程序(如:binvis)
https://code.google.com/archive/p/binvis/
工具:
binwalk – 通过固件文件头来分析文件和文件系统
file – 用来检测是否是有效的文件和文件类型
hexdump --16进制导出工具
strings --跟hexdump类似但是可以以可读的形式展示
lzma --解压LZMA文件
FT232H --多功能 USB to JTAG SWD/UART/FIFO SPI/I2C 模块。也可以使用 JLink、STLink 等调试器。
固件提取
固件安全中首要也是最重要的一部工作就是固件提取,如果提取不到固件,那么后续的测试也很难进行下去。
固件是一个二进制文件的压缩包,文件系统是其中的一个组件,存储在二进制文件的特定偏移地址中,且有一定大小。固件通常由bootloader、内核、根文件系统及其他资源组成。
文件系统类型:squashfs , ubifs , romfs , rootfs , jffs2 , yaffs2 , cramfs , initramfs
bootloader
bootloader的作用主要包括RAM的初始化(存储易失性数据),串口初始化、设备类型检测、内核参数链表设置、initramfs(基于RAM的初始文件系统)加载及内核镜像调用等。Bootloader通过板机支持包(BSP)初始化硬件驱动。简单的说,bootloader和PC的BIOS在启动时的作用是一致的。ARM、MIPS架构架构中常见的bootloader包括redboot、u-boot及barebox等。在bootloader启动内核后,文件系统也就完成了加载过程。
SquashFS
SquashFS是基于Linux内核使用的压缩只读文件系统。该文件系统能够压缩系统内的文档,inode以及目录,文件最大支持2^64字节。每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。inode包含文件的元信息,具体来说有Size 文件的字节数、Uid 文件拥有者的User ID、Gid 文件的Group ID、Access 文件的读写执行权限等。linux可以用stat命令,查看某个文件的inode信息。
IOT设备(尤其是消费级电子设备)最常采用的文件系统是squashfs,分析人员可以使用unsqushfs工具对文件系统提取数据。需要注意的时,有些厂商采用了非标准的squasgfs压缩算法,如LZMA和XZ。
偏移量和文件系统大小信息获取:
- 使用hexdump和grep等工具搜索特征信息
- 判断文件系统开始位置,如:Squashfs文件系统
- 通过hexdump查找 “hsqs” 字符串,
hexdump -C binary | grep -i 'hsqs'
- 通过hexdump查找 “hsqs” 字符串,
- 使用dd命令将从该地址开始到文件末尾的内容全部转储下来
dd if=binary bs=1 skip=92588 of=rt-n300-fs
- 通过如上步骤,从二进制文件中获取到文件系统,使用 unsquashfs 查看整个文件系统
- 判断文件系统开始位置,如:Squashfs文件系统
文件系统头部特征是根据每一种文件系统开头的几字节提炼出来的。常用的文件系统头部特征如下:
- cramfs ---- 文件系统头部特征字符为"0x28cd3d45"。
- squashfs ---- 文件系统头部特征较多,其中一些是标准的squashfs头部,有些是国外的研究人员发现的,大致有sqsh、hsqs、qshs、shsq、hsqt、tqsh、sqlz 7种。
检查是否存在cramfs文件系统头部特征和magic签名“0x28cd3d45"。因为目前不知道文件组织是大端机格式还是小端机格式,所以要进行二次搜索:
strings firmware.bin | grep `python -c 'print "\x28\xcd\x3d\x45"'`
strings firmware.bin | grep `python -c 'print "\x45\x3d\xcd\x28"'`
如果不是cramfs文件类型,那么可以尝试判断是不是Squashfs文件系统:
strings fimware.bin | grep "sqsh" 或 "hsqs"或"qshs"或"shsq"或"hsqt"或"tqsh"或"sqlz"等。
这里如果在strings firmware.bin | grep “hsqs” -----> 打印出hsqs,我们发现了squashfs文件系统"hsqs"的magic签名头,但我们不能完全确定该文件包含的是否为一个squashfs文件系统,还需要进一步确定firmware.bin是不是squashfs文件系统。
对squashsf文件系统的解压、提取,需要借助sasquatch工具,可从https://github.com/devttys0/sasquatch下载安装。
如果sasquatch工具不起作用,也可以使用7-zip对后缀为squashsf的文件进行提取
binwalk
binwalk -ev <bin>
若是文件的标头没有Magic字节 ,需使用 binwalk 查找文件系统的偏移量,然后从二进制文件中分割压缩的文件系统,最后再手动提取出来。
- 分割
dd if=DIR850L_REVB.bin bs=1 skip=1704084 of=dir.squashfs # or
dd if=DIR850L_REVB.bin bs=1 skip=$((0x1A0094)) of=dir.squashfs
- 提取
For squashfs:
unsquashfs dir.squashfs
CPIO archive files:
cpio -ivd --no-absolute-filenames -F <bin>
For jffs2 filesystems:
jefferson rootfsfile.jffs2
For ubifs filesystems with NAND flash:
ubireader_extract_images -u UBI -s <start_offset> <bin>、ubidump.py <bin>
操作方法
这部分借鉴了饭饭大佬的博客
使用芯片夹可以实现 SOP、QFP 封装等针脚外露的芯片免拆固件提取,而对 QFN、BGA 等封装方式则无能无力。
SOP 8/16
SOP(Small Out-Line Package,小外形封装) 针脚从封装两侧引出呈海鸥翼状(L 字形),一般用于针脚不太多的芯片上 。SOP8/16 封装的 Flash 在路由器、摄像头等 IOT 设备厂较为常见。针脚有8根或16根,且一个角上有小圆点(每个芯片都有一个小圆点,小圆点所在的位置是第一针脚,然后逆时针排布第二针脚,第三针脚等),型号代码一般是24或25开头。SOP8/16 SPI Flash 一般的针脚定义如下。
QFP
QFP(Plastic Quad Flat Package,方型扁平式封装技术)针脚从四个侧面引出呈海鸥翼(L)型。实现的芯片针脚之间距离很小,管脚很细,一般大规模或超大规模集成电路采用这种封装形式,其针脚数一般都在64以上。在小型IOT设备中可作为主控或协处理器。下图中的芯片就是采用 QFP 封装。
此类封装由于针脚数不固定,没有统一标准,一般由厂商自行定义,引脚定义详见芯片手册。
BGA
BGA(Ball Grid Array Package,球栅阵列封装) 封装的 I/O 端子以圆形或柱状焊点按阵列形式分布在封装下面,具有更小的体积,更好的散热性能和电性能。由于 I/O 端子在封装下方,提取固件则需要使用BGA返修台拆卸下来读取,不能实现免拆提取。BGA 封装一般用于 CPU 和大容量存储。
此类封装同 QFP 封装的芯片一样,引脚定义需看看相应的芯片手册。
固件文件系统提取基本原理
提取固件二进制镜像中的根文件系统,是对固件进行分析的前提条件,以binwalk提取固件文件系统为例,通过binwalk自带的强大的magic特征库,对固件文件系统初始位置的特征码进行匹配,若匹配成功,将起始地址为开头的整段数据dump下来。
从Flash芯片中读取固件
将导线连接到芯片的引脚,通过飞线连接RT809F编程器,进行编程器软件在线读取固件【该方法比较适合大型的、成本较高、价格昂贵的工控设备,该方法不会对开发板造成破坏】
把芯片拆焊下来,用Flash编程器读取,离线读取固件【适用于芯片引脚采用点锡丝网焊接在背面,引脚太小或没有暴露出引脚等情况】
工具:
flashrom: flashrom 是一个开源的跨平台的烧录工具, 提供了不少的芯片支持和编程器支持. 用于识别、 读取、写入、 验证和擦除各种 Flash 芯片。
通过串口或调试接口读取固件
bootloader读取,芯片和电脑之间通过UART串口连接通信,运行mcuisp软件,点击读Flash即可(或者使用SSCOM软件也可以);
需要用到硬件调试工具,一般可以通过下面两类调试接口,把Flash中的数据读取出来:
(1)SWD接口,利用硬件:J-LINK OB或者J-LINK或者ST-LINK;
利用软件:J-LINK驱动程序自带的J-FLASH或者ST官网提供的STVP。
(2)JTAG接口,利用硬件:J-LINK;利用软件:J-LINK驱动自带的J-FLASH。
文件系统分析
在提取到文件系统后,我们就需要对固件系统进行分析,一般来说,就是先进行静态分析,静态分析又可以考虑手动分析或者借助一些自动化的工具,后面可以使用仿真工具配合IDA或者GDB等二进制分析工具进行动态分析。
手动分析
静态分析文件系统可从如下方面入手:
- 不安全网络守护程序,如:telnetd(有时会伪装成重命名二进制文件)
- 硬编码凭证(用户名、密码、API密钥、SSH密钥和后门变体)
- 硬编码的API端点和后端服务器详细信息
- 更新可用作入口点的服务器功能
- 查看未编译的代码并启动脚本以执行远程代码
- 提取已编译的二进制文件,使用反汇编程序脱机分析
同时,此过程中分析的结果,可为动态分析做基础准备。
自动分析工具可以参考firmwalke/FACT
firmwalker/FACT – 自动化遍历固件文件系统中的所有敏感文件内容
二进制文件分析
可使用工具: IDA Pro、Ghidra、Hopper、Capstone 或 binary Ninja 进行分析
固件模拟
这部分可以直接手动使用qemu,unicorn或者qiling等工具都可以
看到OWASP固件安全测试指南描述可以使用自动化工具进行固件的完整仿真
自动化工具:firmadyne、固件分析工具包、ARM-X 固件仿真框架,这些工具实质上是 QEMU 和其他环境功能 (如:nvram )的包装器。
- https://github.com/attify/firmware-analysis-toolkit
- https://github.com/therealsaumil/armx/
- https://github.com/firmadyne/firmadyne
- https://github.com/qilingframework/qiling#qltool
动态分析
设备在正常运行或者在仿真环境中运行中的动态测试,此阶段的测试可能会由于项目和访问级别有所差异。
分析手段:
篡改引导程序配置
Web 和 API 测试
模糊测试(网络和应用程序服务)
使用各种工具集进行的主动扫描以获取提升的访问权限或代码执行
嵌入式Web应用程序测试
检查方向:
- 诊断和故障排除页面可能存在命令注入
- 验证和授权方案对整个固件中的应用程序和操作系统平台的相同框架进行验证
- 默认的用户名、密码
- 在网页执行目录遍历或文件读取,以识别调试或测试功能
- 在 SOAP/XML 和 API 传输中的输入检查 ,如:XSS 和 XXE
- 跟踪观察应用程序中的参数查看异常点和堆栈溢出点
- 常见的 C/C++ 漏洞、常见的嵌入式 Web 应用程序的有效负载,如:内存损坏漏洞、格式字符串缺陷、整数溢出
引导加载程序测试
修改设备的引导加载程序时,可以进行如下操作:
- 在引导过程中加 “0”、空格、或其他标识的“Magic代码”来获取 shell
- 修改配置以执行 shell 命令,如:引导参数末尾 “init=/bin/sh”
#printenv
#setenv bootargs=console=ttyS0,115200 mem=63M root=/dev/mtdblock3
mtdparts=sflash:<partitiionInfo> rootfstype=<fstype> hasEeprom=0 5srst=0 init=/bin/sh
#saveenv
#boot
设置一个 tftp 服务器,从本地通过网络加载远程图像(前提是设备有网络访问权限)
#setenv ipaddr 192.168.2.2 #local IP of the device
#setenv serverip 192.168.2.1 #tftp server IP
#saveenv
#reset
#ping 192.168.2.1 #check if network access is available
#tftp ${loadaddr} uImage-3.6.35 #loadaddr takes two arguments: the address to load the file into and the filename of the image on the TFTP server
-
使用 ubootwrite.py 编写 uboot-image 并且安装修改过的固件来获取 root
-
查看启用的调试功能,如:详细记录、加载任意内核、从不受信任的来源引导
-
使用警告:使用引脚连接主板,观察设备启动顺序,在内核解压缩之前,将连接主板的引脚短路或者连接到 SPI 闪存芯片上的数据引脚(DO)
-
使用警告:使用引脚连接主板,观察设备启动顺序,在内核解压缩之前,在 U-boot 对 UBI 映像解压缩时,将连接主板的引脚短路或连接到 NAND 闪存芯片的引脚 8 和 9
- 在短接引脚之前请查看 NAND 闪存芯片的数据表
-
使用恶意参数配置恶意 DHCP 服务器作为设备在 PXE 引导期间提取的输入
- 使用 Metasploit DHCP 辅助服务器,进行命令注入,比如修改参数 FILENAME 为 a";/bin/sh;#,来测试设备启动过程的输入验证
参考:
https://scriptingxss.gitbook.io/firmware-security-testing-methodology/v/zhong-wen-fstm/
https://www.freebuf.com/articles/ics-articles/262454.html
https://www.cnblogs.com/from-zero/p/12355492.html#:~:text=%E5%9B%BA%E4%BB%B6%E8%8E%B7%E5%8F%96%E4%B8%BB%E8%A6%81%E6%9C%89%E4%B8%89,%E5%B0%86%E5%9B%BA%E4%BB%B6%E6%8F%90%E5%8F%96%E4%B8%8B%E6%9D%A5%E3%80%82
https://delikely.github.io/2021/01/11/%E5%85%8D%E6%8B%86%E8%8A%AF%E7%89%87%E6%8F%90%E5%8F%96%E5%9B%BA%E4%BB%B6/