跳到主要内容

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 设备策略变量

验证应在测试租户中区分四项结果:能否提取秘密、能否建立新连接、能否访问本机资源、能否访问其他设备资源。处理泄露后,还要检查旧连接与已签发令牌是否继续可用,不能只看控制台上的证书状态。

关联阅读:调试接口与量产权限、设备绑定与所有权转移。