IoT 存储与设备秘密:一台设备失守为何可能影响整批产品
从设备里读出一个字符串,可能只得到 Wi-Fi 配置,也可能得到可冒充设备的私钥,甚至得到能为整批产品签发更新的发布秘密。风险评估需要先确认秘密代表哪项权限,再判断读取难度。
实操:解开官方固件,定位配置、证书和秘密的来源
先按固件与工具准备下载 OpenWrt 官方 SquashFS 根文件系统。它是真实发布镜像,用于学习固件结构;不能预设其中存在设备私钥或可利用漏洞。自己的路由器、摄像头副本按相同流程先识别格式。
确认格式并提取文件系统
在 Ubuntu 工作机的 iot-research 目录执行:
gzip -dk openwrt-24.10.5-x86-64-generic-squashfs-rootfs.img.gz
file openwrt-24.10.5-x86-64-generic-squashfs-rootfs.img
unsquashfs -s openwrt-24.10.5-x86-64-generic-squashfs-rootfs.img
unsquashfs -d squashfs-root openwrt-24.10.5-x86-64-generic-squashfs-rootfs.img
ls squashfs-root/etc
unsquashfs -s 应识别 SquashFS 并报告压缩与文件系统参数。提取目录应能看到 Linux 系统目录、OpenWrt 配置和软件包数据。重新提取时换一个空目录,不把不同版本合并到一起。工具来自 SquashFS 官方项目。
把文件路径追到首次启动行为
解包后先读取 /etc/config/uhttpd 中证书与密钥路径,再在 /etc/init.d/uhttpd 查找对应文件不存在时的分支;对 Dropbear 则对照 /etc/config/dropbear 与 /etc/init.d/dropbear。这样可以把“配置引用了密钥路径”和“发布固件已经带有私钥”分开。
rg -n 'cert|key|generate|openssl|px5g' squashfs-root/etc/config/uhttpd squashfs-root/etc/init.d/uhttpd
rg -n 'key|keygen|dropbear' squashfs-root/etc/config/dropbear squashfs-root/etc/init.d/dropbear
接着在同版本运行固件中检查被引用的文件,比较首次启动前后是否出现、重启是否保持,以及另一个独立设备是否相同。对证书比较公钥或指纹,对私钥记录文件来源和权限;没有对应运行设备时,只能完成生成逻辑的静态追踪。
搜索候选并追踪谁使用它
rg -n -i --hidden 'BEGIN .*PRIVATE KEY|password|passwd|token|secret|api[_-]?key' squashfs-root/etc squashfs-root/usr
rg --files --hidden squashfs-root | rg '\.(pem|key|crt|p12|pfx)$|shadow$|dropbear|uhttpd'
cat squashfs-root/etc/config/uhttpd
第一条命中的变量名和设置脚本不等于泄露凭据。对每项候选继续查其读取代码、配置引用和生成时机:是首次启动随机生成、用户设置、厂商烧录,还是每台设备相同的固定值。公开固件没有现场设备的运行时秘密是正常结果。
OpenWrt 的运行配置可能位于可写 overlay,不能只看只读根文件系统。在自己的 OpenWrt 样机或QEMU 固件中,另行核对 /etc/config/、/etc/dropbear/、证书路径与对应 /overlay/ 数据。公开原始镜像和用户备份应分开保存。
验证复制的是文件还是身份能力
证书可用 openssl x509 -in device.crt -noout -subject -issuer -serial -fingerprint -sha256 查看身份;文件名改成真实证书路径。私钥或对称秘密需要在产品的测试租户上验证实际能力:用原设备正常执行一次允许操作,再在独立客户端使用同一凭据请求同一资源及另一设备资源,核对服务端审计和设备回执。
不要把证书公钥或历史签名响应当作私钥,也不要只凭字符串叫 token 就判断可接管。报告同时保存来源、主体、有效期、授权范围和撤销后的重测结果。私钥不可导出时,还应核对失陷进程能否调用设备上的签名服务。
提取失败时怎么继续
厂商包不是 SquashFS 原始镜像时,先用 file、十六进制文件头和 Binwalk 定位分区与压缩对象,再按实际偏移处理。提取器错误可能来自包头、UBI、压缩支持或加密,不能直接解释为“没有秘密”。
从外置 SPI NOR 取得可比较的副本
设备没有开放下载口时,外置存储是另一条获取路径。先拍摄芯片丝印、封装方向和一号脚,查该型号数据手册,确认 SPI NOR、容量及工作电压。这里不把 NAND、eMMC 或 I²C EEPROM 当作同一种器件。
选择 flashrom 支持的编程器,按芯片手册连接 CS、CLK、MOSI、MISO、电源与地,确认板上供电与在线总线不会争用。CH341A 某些板卡虽然存储座供电为 3.3 V,信号仍可能接近 5 V,应实际测量并按flashrom 官方硬件说明核对;1.8 V 器件需要匹配的电平与供电方案。
Ubuntu 工作机安装 flashrom 后,以下命令针对已确认兼容的 CH341A SPI 编程器,先识别,再独立读取两遍:
flashrom -p ch341a_spi
flashrom -p ch341a_spi -r flash-1.bin
flashrom -p ch341a_spi -r flash-2.bin
wc -c flash-1.bin flash-2.bin
sha256sum flash-1.bin flash-2.bin
cmp flash-1.bin flash-2.bin
记录检测型号、文件长度与哈希。cmp 退出 0 表示两份逐字节相同;不一致时先排查夹具、供电、线长和总线争用,再分析内容。全 00、全 FF 或稳定重复的错误数据也可能得到相同哈希,因此还要核对器件身份、容量和合理文件结构。在线读取无法稳定时,再评估断开总线或离线读取条件;这部分未在实机执行。
成立前提与能力变化
离线提取需要取得固件、存储副本或可读内存,副本中还要存在可恢复的秘密。克隆进一步要求秘密能被另一执行环境使用,服务端接受这项身份,且授权范围包含目标操作。得到公开证书、读到密文、成功证明身份和访问其他设备,是四项不同结果。
固件可读与秘密可用是两个问题
固件可以来自官网下载、升级缓存、设备文件系统或外置存储读取。找到固件镜像,不等于突破了安全边界:许多产品本就公开分发固件,安全设计应允许攻击者看到程序逻辑。
真正需要保护的是不能公开的材料,以及这些材料的使用路径。设备证书中的公钥可以公开,私钥需要保护;服务器地址通常不是秘密,能访问其他设备的服务凭据才是。把常量编码、压缩或混淆,不会改变攻击者最终能够恢复数据的事实。
可将提取结果分成三层:本机隐私与配置、仅代表本机的身份材料、具有跨设备管理或发布权限的秘密。三层需要不同处置:清除用户数据、吊销设备身份,以及轮换平台或签名体系,不能统一用“修改设备密码”处理。
发布私钥下发给设备会反转信任关系
IOActive 在 2014 年公开的 Belkin WeMo 分析指出,所分析固件包含固件签名所需的秘密材料,使研究者能够对自制内容生成设备接受的签名。这里的失效不是签名算法被破解,而是验证端拿到了本应只由发布端持有的能力。该材料反映历史产品实现,不能用来判断当前型号状态。IOActive 原始披露
正确的方向是设备只持有验证材料,发布侧保留签名私钥。设备之间共享一个对称认证秘密也有类似问题:一台设备被拆取后,攻击者可能冒充其他使用同一秘密的设备。因此,需要核对秘密的作用范围,而不只检查是否使用 AES 或 HMAC。
Flash 加密保护离线读取,不能包办运行时安全
ESP-IDF v5.3.4 的 ESP32 Flash Encryption 文档说明了片外 Flash 加密,并列出默认与可选加密分区。它同时区分生产使用的 Release 模式与开发配置。对具体产品,必须检查分区表和量产设置,不能把“开启 Flash 加密”理解为所有持久化数据均已受到相同保护。ESP32 Flash Encryption
离线读出的密文难以还原,并不意味着运行中的进程无法读取明文。应用正常使用秘密时,解密结果可能进入普通内存、日志、崩溃转储或升级备份。调试权限和应用漏洞能够形成另一条提取路径。
每设备独立密钥能够缩小一台样机失陷后的扩散范围,但生产系统还要解决密钥生成、灌装、备份和报废。若量产日志保留了所有明文密钥,设备上的防护就只覆盖了攻击链的一部分。
用秘密清单区分数据保护与权限保护
以下清单是审计模型,具体权限要到产品与后端配置中确认:
| 材料 | 可能代表的能力 | 泄露后需要检查什么 |
|---|---|---|
| 家庭网络口令 | 加入对应无线网络 | 是否复用,转售或重置后是否残留 |
| 设备身份私钥 | 以设备身份发起认证 | 资源范围、撤销、并发克隆连接 |
| 用户刷新令牌 | 继续领取用户会话 | 用户与设备范围、刷新与撤销机制 |
| 全设备共享秘密 | 伪装同一共享域的成员 | 一台失守是否影响整批设备 |
| 固件发布私钥 | 为受信任软件签名 | 发布链、密钥轮换和离线设备迁移 |
静态加密还需要核对完整性:密文不易读取,不代表攻击者不能修改存储并影响下一次使用。反过来,具有完整性校验的配置也可能仍以明文保存,无法抵御离线读取。机密性与完整性应分别验证。
对每一项秘密,应记录产生位置、持久化位置、解密后出现的地址空间、使用者与销毁方式。这样才能发现“芯片密钥槽保护很好,旁边诊断备份却包含同一秘密”的旁路,也能判断删除文件是否真正清除了可恢复副本。
不可导出密钥仍需限制调用者
安全元件或芯片内的受保护密钥槽,可以让私钥不以普通文件形式出现。但如果已失陷的应用能够无限制请求签名,攻击者仍可能在不拿到密钥字节的情况下使用设备身份。
设计上可以把密钥用途、可签名消息格式、会话挑战和调用方权限一起限制。设备身份也不应自动拥有控制其他设备或领取平台管理凭据的能力。不可导出解决“复制秘密”,授权约束解决“滥用秘密”,两者承担不同职责。
用挑战应答检验“复制文件”是否变成设备克隆
在测试服务中,可以让服务器向待认证端发送一次性挑战,由设备使用身份私钥证明持有能力。具体签名方案与消息编码应由产品协议规定,下面只表达验证关系:
测试服务 → 待认证端:本次挑战、目标服务和会话标识
待认证端 → 测试服务:身份声明、对本次上下文的密码学证明
测试服务:验证受信任身份、挑战未使用、上下文匹配,再检查资源权限
只有证书副本时,攻击者未必能产生有效证明;只有一条历史签名时,新挑战可以使直接重放失败;若已失陷的原设备可以持续代签,则证明会成功,但这仍不同于私钥字节已经被导出。
接下来使用同一身份访问本机和另一台测试设备的资源。若认证成功但跨设备访问被拒绝,说明身份保护失守与资源隔离失守没有同时发生。反过来,如果云策略允许所有设备使用同一高权限身份,修复单机提取路径仍不足以消除其他共享入口。
克隆是否成功,要到依赖身份的服务验证
复制证书文件不一定完成克隆;若没有对应私钥,就无法证明持有身份。即使握手成功,服务端也可能限制该身份只能使用特定设备名称和资源。
AWS IoT Core 的 Thing policy variables 文档给出了一个具体例子:设备注册关系、证书关联、连接的 client ID 与策略变量需要匹配;其中 IsAttached 可用于约束身份与注册设备的关联。实际行为取决于证书关联模式和策略配置。AWS IoT 设备策略变量
验证应在测试租户中区分四项结果:能否提取秘密、能否建立新连接、能否访问本机资源、能否访问其他设备资源。处理泄露后,还要检查旧连接与已签发令牌是否继续可用,不能只看控制台上的证书状态。
关联阅读:调试接口与量产权限、设备绑定与所有权转移。