当前位置: 首页 > news >正文

一个没做安全防护的IoT设备,被客户骂了一下午

"喂,你们那设备上电之后随便拿个串口工具就能读固件?"

客户这句话是下午三点发过来的。当时我正在调试另一块板子,看了一眼消息,心想不至于吧。结果试了一下——好家伙,Reset拉低、BOOT0拉高、上电,OpenOCD连上去二话不说就把整个flash读出来了。客户说得客气,但意思很明确:你们这东西跟裸奔没区别。

说实话之前不是没想过安全问题。但像大多数做单片机开发的团队一样,总觉得"我们这玩意儿又不连公网"、"谁会闲得蛋疼来读你这破固件"——直到这东西真的被客户部署到别人家的机房里,你才发现:信任边界一旦超出你控制的范围,一切假设都不成立


secure boot 这东西,很多低端MCU压根不支持。但如果你用的芯片好歹有个OTP区域或者efuse,那就能做点文章。拿STM32举例子,它的RDP(Read Protection)等级分三级:

  • Level 0:无保护,你随便读
  • Level 1:禁止调试接口读flash,但SRAM还能访问,可以通过SRAM里的代码间接读flash
  • Level 2:彻底锁死,调试接口永久失效,不可逆

我当时第一个反应是直接上Level 2。但一查手册——Level 2一旦设置,连你自己都没办法通过调试器更新固件了,只能走ISP或者OTA。如果bootloader崩了,这板子就只能当砖头扔了。Level 2这玩意儿就是个回不了头的选择。

后来折中了一下,选了Level 1 + 自定义校验。Level 1意味着SWD/JTAG直接读flash读不出来,但还是要防SRAM攻击。STM32有一个叫RDP Regression的功能——如果你设置了Level 1,然后又想降级回Level 0,芯片会自动擦除整个flash。这是个好机制。

但问题在于:光靠硬件读保护还不够。

/* 设置RDP Level 1的代码,很简短 */ HAL_FLASH_OB_Unlock(); FLASH_OBProgramInitTypeDef ob = {0}; ob.OptionType = OPTIONBYTE_RDP; ob.RDPLevel = OB_RDP_LEVEL_1; HAL_FLASHEx_OBProgram(&ob); HAL_FLASH_OB_Launch();

这段代码跑完,重新上电,读保护就生效了。但你知道我踩了什么坑吗?它只保护了flash的静态数据。如果攻击者把你板子上的flash芯片(如果是外部spi flash)直接拆下来用编程器读呢?如果你用的是外部QSPI Flash存代码呢?那就不是RDP能管的事了。

去年有个项目用了W25Q64做代码存储,结果生产的时候发现烧录效率太低,就把flash座子喷了——结果客户现场,维修工直接把座子上的flash拆下来,插到自己的烧录器上读走了固件。物理访问 = 完全控制,这个基本假设你得接受。

所以后来做了这么几件事:

第一,bootloader里加了一层签名验证。在固件bin文件的末尾附上RSA签名,bootloader启动时用公钥验签。公钥放在芯片内部flash的OTP区域,改不了。这样就算固件被读走了,它也没法篡改后刷回去。

/* bootloader验签简化版 */ bool verify_firmware(const uint8_t *fw, uint32_t len) { /* 假设fw的最后256字节是RSA-2048签名 */ uint32_t sig_len = 256; const uint8_t *signature = fw + len - sig_len; uint8_t hash[32]; mbedtls_sha256(fw, len - sig_len, hash, 0); return mbedtls_rsa_pkcs1_verify( &public_key, MBEDTLS_MD_SHA256, hash, signature) == 0; }

别笑,就这一行验签函数,省了后面多少破事。

第二,外部flash的数据做了AES加密。密钥存在MCU内部的OTP里,bootloader启动时用内部flash的OTP key解密外部flash的代码。这样就算别人拆了flash芯片,拿到的也是密文,根本用不了。

/* AES解密外部flash内容到内部SRAM */ void decrypt_firmware(uint32_t ext_addr, uint32_t *dest, uint32_t size) { uint8_t key[32]; read_otp_key(key); /* 从OTP读密钥 */ mbedtls_aes_context aes; mbedtls_aes_setkey_dec(&aes, key, 256); uint8_t iv[16] = {0}; /* 注意这里的iv不能全0,生产环境下要从eFuse/UID派生 */ for (uint32_t i = 0; i < size; i += 16) { uint8_t buf[16]; /* 从外部flash读 */ spi_flash_read(ext_addr + i, buf, 16); mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, 16, iv, buf, dest + i/4); } }

当然AES-CBC模式有个问题:如果攻击者能多次上电,观察不同iv下的解密行为,理论上可以进行padding oracle attack。虽然对嵌入式场景来说攻击成本很高,但如果你的产品要过FIPS或者等保测评,建议换成GCM或者CTR。

第三,加了一道防篡改检测,虽然不是软件层面的——在PCB上埋了几条细走线,一旦外壳被打开,走线被切断,立即触发DMAC(死亡中断),调用RTC backup register里存的标志位,下次启动直接进recovery模式。硬件层级的防护比软件更不好绕过。

那OTA升级的安全性呢?

一旦开启了OTA,攻击面就从一个物理接触口扩大到了网络。最开始我图省事,OTA固件包直接用明文传。直到有次做渗透测试,对方通过抓包拿到了固件镜像,逆向分析找到了里面硬编码的阿里云key……那个云账号下的所有设备数据全等于白送。

那一版之后,OTA包至少要做:签名(防篡改)+ 加密(防窥探)+ 版本号校验(防降级攻击)。缺一个都不行。

OTA包的格式大概是这样的:

| 固件头 (64B) | 固件数据 (加密) | RSA签名 (256B) | |- magic: 4B | | | |- version: 4B | | | |- length: 4B | | | |- iv: 16B | | | |- reserved | | |

bootloader收到OTA包后,先验签名,确认版本不低于当前运行的版本(防止rollback attack),然后解密写入flash。

说到防降级攻击,我见过一个真实案例:某厂商的智能门锁,攻击者拿到了旧版本的固件包(旧版本有个硬编码的管理员口令),然后模拟服务器伪装成OTA下发,把门锁降级到了那个版本,直接拿管理员权限开门。所以版本号校验是硬性要求,而且版本号要跟签名一起验证,不能被篡改。

最后说一句:安全这种事,没有银弹。你加了读保护、上了签名验证、做了加密存储——还是会有人拿探针去刮开芯片封装,用FIB(聚焦离子束)修改变压器电路来绕过OTP熔丝。但问题是,你的攻击者愿意花多少钱来搞你

对于大部分IoT设备来说,把防护成本做到比你的产品售价高,就已经赢了。几千块钱买回来的设备,攻击者不太可能花几万美金去做芯片级逆向。除非你的设备控制的是核电站阀门或者几百万人的支付数据——那另当别论。

对了,后来我跟客户说已经加了读保护和签名验证。他没回消息。第二天他发了一张照片过来——一台用钢锯锯开的设备壳子,旁边丢着个逻辑分析仪。底下配文:"这个怎么防?"

http://www.jsqmd.com/news/1106862/

相关文章:

  • Diablo Edit2:5大核心技术突破重塑暗黑破坏神II角色编辑体验
  • 终极跨平台B站观影指南:让你的游戏机变身视频中心
  • 洛谷-P11403 [RMI 2020] 软盘 / Floppy 题解
  • 高光谱相机全解析:技术分类、主流品牌与选型指南
  • 连锁超市收银系统选什么?四大品牌深度横评与避坑指南
  • Java Stream、File与IO-核心场景实战
  • NifSkope 3D模型编辑器:专业游戏模型处理完全指南
  • 国内物流包装垂直随机振动试验优先选用 GB/T 4857.23-2021 附录 D 说明
  • 【课程设计/毕业设计】基于 SpringBoot 的校园日常行为规范评分归档系统的设计与实现 基于 SpringBoot 的中小学学生品行综合考评管理系统【附源码、数据库、万字文档】
  • 越华环保集团资质元数据治理体系与项目准入校验架构设计
  • 第一章Netty,Selector写入内容过多问题
  • 4-20mA电流环接收器设计与STM32高精度ADC实现
  • .net环境下跨进程、高频率读写数据
  • Windows系统文件AutomaticAppSignInPolicy.dll丢失找不到问题解决
  • React Native 0.86 亮点速览:边到边修复、DevTools 深色模拟、JSI 再增强
  • 技术人转型项目管理:30岁前后如何用PMP完成思维切换
  • 当下游master被污染后,如何与上游master进行同步
  • 计算机Java毕设实战-基于 SpringBoot 的中小学学生德育行为考评系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 科研选题不再受限于抗体缺货,云克隆全链条自研如何支撑百万种衍生资源
  • 免费开源桌面分区神器:5分钟彻底告别杂乱Windows桌面
  • Docker容器的跨节点通信
  • Linux基础文件与目录实操学习笔记
  • 塞尔达传说:旷野之息存档编辑器终极指南 - 5分钟掌握海拉鲁世界修改秘籍
  • 云手机技术详解:原理、自动化 API 实战代码与商用选型指南
  • 说明Svcb到外部服务的通信被打通了!
  • 【毕业设计】基于 SpringBoot 的学生日常表现评分登记管理系统的设计与实现 基于 SpringBoot 的中小学行为规范考核管理系统(源码+文档+远程调试,全bao定制等)
  • 返回主页I WOULD NEVER DIE FOR MY BELIEFS BECAUSE I MIGHT BE WRONG
  • 机器学习模型生产就绪:从Notebook到高可用服务的系统化实践
  • 临床AI风险分层模型:从电子病历挖掘生存期预测信号
  • 让AI读懂你的企业:云境标书AI在招投标场景下RAG与知识图谱的工程实践