3. 固件漏洞挖掘:静态分析、动态分析与模糊测试入门

固件漏洞挖掘,说白了就是跟藏在二进制里的“地雷”较劲。我做了这么多年固件安全,最深的体会是:没有银弹。静态分析、动态分析、模糊测试,这三板斧各有各的脾气,你得知道什么时候用哪把刀。

这一章,我就带你把这三种方法的核心逻辑捋一遍。不扯太深的理论,重点是你上手就能用的东西。

3.1 静态分析技术:不运行代码,先看“面相”

静态分析,就是对着二进制文件直接开刀。你不需要跑设备,不需要搭环境,拿到固件就能开始翻。我个人习惯,拿到一个新固件,第一件事就是跑一遍静态分析,先摸清它的底细。

3.4.1 文件系统提取与识别

大部分固件都是打包好的文件系统镜像。你得先把它拆开,才能看到里面的配置文件、脚本和二进制程序。

  • binwalk:这工具我几乎天天用。它能自动识别固件里的文件系统类型和偏移量。
  • dd:配合 binwalk 的结果,用 dd 把特定分区切出来。
  • unsquashfs / jefferson:解压 SquashFS 或 JFFS2 文件系统。

实战命令示例:

# 扫描固件,识别文件系统
binwalk firmware.bin

# 提取指定偏移量的分区
dd if=firmware.bin of=rootfs.squashfs bs=1 skip=0x123456

# 解压 SquashFS
unsquashfs rootfs.squashfs

我曾经遇到过一个固件,binwalk 死活识别不出来。后来发现是厂商在文件头前面加了一段自定义的垃圾数据。手动跳过之后,才看到里面的 SquashFS 镜像。嗯,这种“小把戏”其实挺常见的。

3.1.2 字符串与硬编码秘密

解压完文件系统,第一件事就是翻字符串。很多漏洞,其实就藏在那些不该出现的明文里。

  • 硬编码凭据:用户名、密码、Token、API Key。
  • 调试后门:telnetd、debug shell、未授权的 SSH 密钥。
  • 敏感路径:/tmp/update.sh、/var/run/backdoor.pid。

我的小技巧:用 strings 配合 grep 过滤关键词,比如 "password"、"secret"、"key"、"admin"。但别只看这些明显的词。我见过有人把密码藏在 "p@ssw0rd" 或者 "s3cr3t" 这种变体里。多想想攻击者的思路。

3.1.3 反汇编与交叉引用

字符串只是线索。真正要挖漏洞,你得看代码逻辑。这时候就需要反汇编器了。

  • Ghidra:开源,功能强大,我最近的主力工具。
  • IDA Pro:商业工具,插件生态丰富,但贵。
  • Radare2 / Rizin:命令行流,适合自动化脚本。

静态分析的核心,是找“危险函数”。比如 strcpy、sprintf、system、popen。你想想看,如果这些函数的参数来自用户输入(比如 HTTP 请求、MQTT 消息),那基本就是缓冲区溢出或命令注入的温床。

注意:静态分析只能告诉你“这里可能有漏洞”,但不能证明它一定能被利用。因为有些危险函数可能被安全的上层逻辑保护着。所以,静态分析的结果,需要动态分析来验证。

3.2 动态分析技术:让代码跑起来,看它怎么“死”

静态分析是纸上谈兵,动态分析才是真刀真枪。你得让固件在模拟器或真实硬件上跑起来,然后观察它的行为。

3.2.1 固件模拟:用 QEMU 跑起来

不是所有固件都能直接模拟。但大部分基于 Linux 的嵌入式固件,都可以用 QEMU 的用户模式或系统模式跑起来。

  • 用户模式模拟:只模拟单个二进制程序,适合分析 Web 服务、后台守护进程。
  • 系统模式模拟:模拟整个硬件平台,适合需要完整内核环境的场景。

用户模式模拟示例:

# 假设你提取出了 ARM 架构的 httpd 程序
# 使用 QEMU 用户模式运行它
qemu-arm-static ./bin/httpd

我记得有一次,我模拟一个 MIPS 架构的路由器固件。QEMU 启动后,Web 服务一直报错。折腾了半天,发现是固件里有个硬件 watchdog 定时器,模拟器里没有对应的硬件,导致系统不断重启。最后我 patch 掉了 watchdog 的初始化代码,才顺利跑起来。这种坑,你迟早也会遇到。

3.2.2 运行时监控:看它调用了什么

程序跑起来之后,你得监控它的行为。常用的手段有:

  • strace:跟踪系统调用。看它打开了哪些文件,连接了哪些网络地址。
  • ltrace:跟踪库函数调用。看它调用了哪些危险函数。
  • GDB:动态调试。设置断点,查看内存和寄存器状态。

避坑指南:我曾经在分析一个 UPnP 服务时,用 strace 发现它在处理 SSDP 请求时,会调用 system() 执行一个拼接的字符串。这就是典型的命令注入漏洞。如果你只做静态分析,可能只会看到 system() 这个函数,但不知道它的参数是否可控。动态分析能帮你确认这一点。

3.2.3 网络流量分析

很多固件漏洞,最终是通过网络触发的。所以,抓包分析也是动态分析的重要一环。

  • tcpdump / Wireshark:抓取网络流量,分析协议格式。
  • Burp Suite:专门分析 HTTP/HTTPS 流量,适合 Web 接口。
  • 自定义 Fuzzer:构造畸形数据包,发送给目标服务。

3.3 模糊测试入门:让机器帮你找漏洞

静态和动态分析,都需要你手动去猜漏洞点。但模糊测试不一样——它让机器自动生成大量畸形输入,然后观察目标会不会崩溃。

3.3.1 模糊测试的基本原理

模糊测试的核心就三步:

  1. 生成输入:基于模板或协议规范,生成大量变异数据。
  2. 发送输入:把数据发送给目标程序或设备。
  3. 监控异常:检测目标是否崩溃、挂起、或产生异常输出。

你想想看,人工测试一天可能测几百个用例。但一个 Fuzzer 一秒钟就能生成几万个用例。这就是效率的碾压。

3.3.2 常用 Fuzzer 工具

工具 适用场景 特点
AFL (American Fuzzy Lop) 本地二进制程序 基于覆盖率引导,效率高
LibFuzzer 库函数测试 集成在 LLVM 中,适合单元测试
Boofuzz 网络协议 Fuzz Python 编写,易于扩展
Mutiny 网络协议 Fuzz 基于 PCAP 重放,适合未知协议

一个简单的 Boofuzz 脚本示例:

from boofuzz import *

def main():
    session = Session(
        target=Target(connection=SocketConnection("192.168.1.1", 80, proto="tcp"))
    )

    define_protocol(session)
    session.fuzz()

def define_protocol(session):
    s_initialize("HTTP Request")
    s_static("GET /")
    s_string("FUZZ", fuzzable=True)  # 这里会被变异
    s_static(" HTTP/1.1\r\n")
    s_static("Host: 192.168.1.1\r\n")
    s_static("\r\n")

if __name__ == "__main__":
    main()

3.3.3 模糊测试的局限性

模糊测试不是万能的。它有几个明显的短板:

  • 覆盖率问题:如果代码路径很深,或者需要特定的状态才能触发,Fuzzer 可能永远走不到那里。
  • 协议复杂性:对于有状态、有校验和的协议,Fuzzer 生成的畸形包可能直接被协议栈丢弃,根本到不了目标函数。
  • 资源消耗:跑一个 Fuzzer 可能需要几天甚至几周的时间,而且需要大量的 CPU 和内存。

注意:我曾经用 AFL 跑一个嵌入式 Web 服务器,跑了三天,一个 crash 都没找到。后来我手动分析了一下,发现 Fuzzer 生成的输入大部分都在 HTTP 解析的早期阶段被过滤掉了。我调整了输入模板,加入了合法的 HTTP 头部,才让 Fuzzer 穿透到更深层的处理逻辑。所以,Fuzzer 不是傻瓜式工具,你得懂它的脾气。

3.4 三种技术的协同:我的工作流

静态分析、动态分析、模糊测试,不是互相替代的关系,而是互补的。我个人的工作流是这样的:

  1. 先用静态分析摸清结构:提取文件系统,搜索硬编码秘密,反汇编关键二进制,找出所有可能的攻击面(Web 接口、UPnP、MQTT、自定义协议)。
  2. 再用动态分析验证攻击面:模拟固件,用 strace 和 GDB 观察关键函数的输入输出,确认哪些输入点是可控的。
  3. 最后用 Fuzzer 批量挖掘:针对确认的攻击面,编写 Fuzzer 模板,进行大规模测试。

这个流程,我用了很多年,效果一直不错。你刚开始做的时候,可能会觉得静态分析太慢,或者 Fuzzer 跑不出结果。别急,慢慢来。每个漏洞的发现,背后都是大量的试错和耐心。

我的建议:别一开始就想着挖 0day。先从已知漏洞的 CVE 复现开始练手。比如去搜索某个路由器型号的历史漏洞,下载对应的固件版本,用我上面说的方法去复现它。当你成功复现一个漏洞时,那种成就感会推着你继续往下走。

固件漏洞挖掘:三种技术协同工作流 静态分析 动态分析 模糊测试 文件系统提取 字符串分析 反汇编 QEMU 模拟 strace/GDB 网络抓包 AFL Boofuzz LibFuzzer 验证 批量挖掘 工作流说明 1. 静态分析:先摸清固件结构,找出所有攻击面。 2. 动态分析:在模拟环境中验证攻击面是否可控。 3. 模糊测试:针对确认的攻击面,进行大规模自动化测试。 三者协同,才能最高效地发现固件中的安全漏洞。