4、Boot ROM 代码安全风险分析

Boot ROM 是芯片上电后执行的第一段代码。它不可修改,所以一旦有漏洞,后果很严重。我这些年做安全审计,见过太多因为 Boot ROM 出问题导致整个产品线翻车的案例。今天咱们就聊聊四种最常见的攻击方式。

核心观点:Boot ROM 的安全风险不是理论问题,而是实打实的工程问题。攻击者只要找到一条路绕过校验,你的安全体系就形同虚设。

4.1 代码注入攻击

代码注入,说白了就是攻击者想办法让 Boot ROM 执行它不该执行的代码。Boot ROM 通常从外部存储(比如 SPI Flash)加载下一级镜像。如果这个加载过程有漏洞,攻击者就能塞入恶意代码。

我遇到过最典型的场景是这样的:Boot ROM 从 Flash 读取镜像头部信息,然后根据头部里的长度字段去拷贝数据。如果攻击者把长度字段改成一个超大值,或者改成负数,就会触发缓冲区溢出。

// 有漏洞的伪代码示例
uint32_t image_size = read_from_flash(HEADER_OFFSET + 8);
uint8_t *buffer = (uint8_t *)SRAM_BASE;
for (uint32_t i = 0; i < image_size; i++) {
    buffer[i] = read_from_flash(DATA_OFFSET + i);
}

你看,这里没有做任何边界检查。如果 image_size 大于 SRAM 大小,数据就会写到 SRAM 之外,覆盖掉关键数据区甚至代码区。

避坑指南:我曾经在一个 IoT 芯片项目里发现,Boot ROM 的镜像加载函数居然用了 memcpy,而且目标地址是固定的栈变量。攻击者只要精心构造镜像,就能让返回地址指向攻击者控制的区域。那次修漏洞改的是 Boot ROM 的 mask,代价是六位数美金。

代码注入的常见入口点:

  • 镜像头部解析:长度、偏移量、校验值等字段未做范围检查
  • 解压缩逻辑:解压过程中如果输入数据可控,可能触发堆栈溢出
  • 签名验证回调:某些实现允许加载额外的公钥或证书,如果解析有漏洞就能注入
  • 调试接口:JTAG/SWD 如果没完全锁死,攻击者可以直接注入指令

4.2 侧信道攻击

侧信道攻击不直接攻击代码逻辑,而是通过物理量来偷信息。功耗、电磁辐射、执行时间,这些都是侧信道。Boot ROM 里最容易被攻击的是密码学操作,尤其是签名验证。

你想想看,Boot ROM 验证镜像签名时,通常会用公钥去解密哈希值,然后比对。如果这个比对过程是逐字节进行的,攻击者就能通过测量执行时间来推断出正确的哈希值。

个人经验:我审计过一个芯片,它的 RSA 签名验证用了标准的蒙哥马利模乘,但没有做时间恒定处理。攻击者用简单的功耗分析,就能在 1000 次采样内恢复出私钥。嗯,那个项目后来不得不紧急换芯片。

侧信道攻击的常见类型:

攻击类型 利用的物理量 Boot ROM 中的脆弱点
时序分析 执行时间 非恒定时间的比较操作、条件分支
简单功耗分析 (SPA) 瞬时功耗 密钥位相关的操作序列差异
差分功耗分析 (DPA) 统计功耗 对称加密算法的 S 盒查找
电磁分析 (EMA) 电磁辐射 局部区域的加密运算

为什么 Boot ROM 特别怕侧信道?因为 Boot ROM 的代码是固定的,攻击者可以反复触发同样的操作,采集大量样本做统计分析。而且 Boot ROM 阶段通常没有操作系统,没有进程隔离,攻击者能获得非常干净的信号。

4.3 故障注入攻击

故障注入攻击,就是通过外部手段让芯片在运行时出错。常见的手段有:电压毛刺、时钟毛刺、电磁脉冲、激光照射。攻击者希望芯片在某个关键判断时跳错分支,或者跳过某条指令。

Boot ROM 里最容易被故障注入的点,就是条件分支。比如签名验证通过后,会设置一个标志位,然后检查这个标志位。如果攻击者在检查指令的瞬间注入故障,让比较结果翻转,那就能绕过签名验证。

// 故障注入的目标代码
if (signature_valid == TRUE) {
    jump_to_next_stage();   // 攻击者希望跳过这个判断
} else {
    halt_forever();
}

我见过一个真实的案例:某款手机芯片的 Boot ROM 在验证完镜像后,会读取一个 GPIO 电平来决定是否进入下载模式。攻击者只要在读取 GPIO 的瞬间注入一个电压毛刺,就能让芯片误以为进入了下载模式,从而加载任意代码。

重要提醒:故障注入不是科幻电影里的东西。一个 50 块钱的电压毛刺发生器就能攻破很多芯片。我建议你在设计 Boot ROM 时,默认假设攻击者有能力注入单比特故障。

故障注入的防御思路:

  • 冗余检查:关键判断做两次,或者用不同的方式验证同一件事
  • 时间随机化:在关键操作中插入随机延迟,让攻击者难以对准故障窗口
  • 传感器检测:片上集成电压/时钟/温度传感器,检测到异常时复位
  • 错误传播:一旦检测到故障,不要立即报错,而是继续执行但最终导向安全状态

4.4 回滚攻击

回滚攻击,就是攻击者让芯片加载一个旧版本的镜像。旧版本可能有已知漏洞,攻击者可以利用这些漏洞来提权或者解锁设备。

Boot ROM 通常只验证签名的合法性,不验证版本的新旧。如果攻击者手里有一个旧版本的合法签名镜像,他就能让芯片降级到那个版本。

我记得有个路由器芯片就是栽在这上面。它的 Boot ROM 只检查签名是否来自官方私钥,不检查版本号。攻击者拿到了一个两年前的固件,里面有个缓冲区溢出漏洞,直接 getshell。后来官方不得不发声明说「请用户不要使用旧版本固件」——你想想看,这能防住谁?

关键点:回滚攻击的根源在于 Boot ROM 没有版本管理机制。它只认「谁签的」,不认「什么时候签的」。

防止回滚攻击的常见方法:

  1. 版本号熔丝:在芯片 OTP 中烧录一个最低允许版本号,Boot ROM 加载镜像时比较版本号,低于熔丝值的拒绝启动
  2. 单调计数器:每次更新固件时递增一个不可回滚的计数器,Boot ROM 检查镜像中的计数器值
  3. 版本绑定签名:签名时把版本号作为签名输入的一部分,这样旧版本的签名在新版本上无效

这三种方法各有优劣。熔丝方案最简单,但一旦烧错就废了。单调计数器需要额外的硬件支持。版本绑定签名最灵活,但需要管理好签名密钥的版本信息。

4.5 四种攻击的关系与防御优先级

这四种攻击不是孤立的。攻击者经常组合使用:先用故障注入绕过签名验证,然后注入代码,再通过侧信道窃取密钥,最后用回滚攻击维持持久化访问。

下面这张图展示了四种攻击在 Boot ROM 执行流程中的攻击面:

Boot ROM 安全攻击面总览 Boot ROM 安全攻击面总览 上电复位 Boot ROM 启动 加载外部镜像 签名验证 跳转下一级 代码注入攻击 侧信道攻击 故障注入攻击 回滚攻击 说明: 红色=代码注入,紫色=侧信道,橙色=故障注入,青色=回滚攻击。箭头指向攻击发生的阶段。

从防御优先级来看,我个人建议这样排:

  1. 先防代码注入:这是最直接的攻击方式,也是门槛最低的。把输入校验做好,把边界检查做全,能挡住 80% 的攻击者。
  2. 再防回滚攻击:这个实现成本相对低,加个版本号检查就行。但一旦漏了,后续所有安全加固都白费。
  3. 然后防故障注入:需要硬件支持,但可以在 Boot ROM 层面做软件冗余。
  4. 最后防侧信道:这个最难,也最贵。如果芯片不是做高安全场景(比如支付、身份认证),可以适当降低优先级。

我的建议:别想着一次性把所有攻击都防住。先评估你的芯片用在什么场景,攻击者的能力和成本是多少。一个智能灯泡和一台 ATM 机的安全需求完全不同。我见过太多团队在侧信道上花了大量精力,结果代码注入的漏洞都没堵住——这就有点本末倒置了。